> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.cloudraker.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.cloudraker.com/_mcp/server.

# Process API

The `/process` API is a one-call ingestion pipeline. You send files and an optional list of actions in a single multipart request; CloudRaker parses each file, runs the actions, holds the results for a TTL you choose, and then auto-purges everything. You get back a processing id to poll — and, if you want, [signed webhooks](/developers/webhooks) as each event happens.

It's the fastest way to turn documents into structured data from your own backend, with no spaces or manual file management to wire up.

For new integrations, prefer the JSON capability endpoints — [extract](/capabilities/extract) and [parse](/capabilities/parse). They take a file URL or id instead of multipart, return the result in the same call, and cite every extracted field. `/process` stays supported and unchanged for the integrations already on it.

Authorization is org-level: the caller must be an **org API key** or an **org admin**.

## The flow

#### Start a pipeline

`POST /process` with your files and options.

#### Poll for status and results

`GET /process/{id}` until `status` is `done` (or `failed` / `expired`).

#### (Optional) purge early

`DELETE /process/{id}` cancels in-flight work and deletes everything now — or let it expire on its own.

## Start a pipeline

`POST /process` takes `multipart/form-data`: one part named **`options`** (JSON) plus one part per file you declare.

### The `options` part

```jsonc
{
  "files": [
    { "field": "invoice", "processingKind": "doc-auto" }
  ],
  "actions": ["<installedActionId or slug>"],   // optional; default []
  "callbackUrl": "https://example.com/webhooks/rakerone",  // optional
  "durationSeconds": 86400              // optional TTL; default 86400 (24h), max 604800 (7d)
}
```

* **`files[]`** — one entry per file. `field` must match the name of a multipart part carrying that file's bytes. `processingKind` is optional.
* **`actions[]`** — the installed actions to run against the ingested files. Each entry is an installed action's **id or its per-organization slug** — the two are interchangeable everywhere the API takes an installed action. Optional; defaults to none.
* **`callbackUrl`** — a receiver for [signed webhook](/developers/webhooks) events. Optional.
* **`durationSeconds`** — how long results live before auto-purge. Default 24h, max 7 days.

`processingKind` is one of:

| Value                          | For                                          |
| ------------------------------ | -------------------------------------------- |
| `doc-simple`                   | Text-native documents, fastest path          |
| `doc-ocr`                      | Scanned / image documents needing OCR        |
| `doc-auto`                     | Let CloudRaker choose between simple and OCR |
| `audio-transcribe`             | Audio → transcript                           |
| `audio-transcribe-and-diarize` | Audio → transcript with speaker labels       |

### Request

```bash
curl -X POST https://api.cloudraker.com/process \
  -H "Authorization: Bearer $RAKERONE_API_KEY" \
  -F 'options={"files":[{"field":"invoice","processingKind":"doc-auto"}]};type=application/json' \
  -F 'invoice=@./invoice.pdf'
```

### Response — `201`

```json
{
  "processingId": "…",
  "expiresAt": "2026-07-21T00:00:00.000Z",
  "statusUrl": "/process/<processingId>"
}
```

Pipeline order: each file is ingested and parsed → any declared actions are dispatched → results are collected → webhooks are emitted → everything is held until the TTL, then auto-purged.

The request body is capped by the platform (on the order of 100 MB). For large files, register them into a space with the [presigned upload flow](/developers/files-and-uploads) instead, which streams straight to object storage.

Possible errors: `400` (malformed multipart or manifest), `403` (caller is neither an org admin nor an org API key).

## Poll for status and results

### Request

GET [https://api.cloudraker.com/process/\{id}](https://api.cloudraker.com/process/\{id})

```curl
curl https://api.cloudraker.com/process/id \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json"
```

```python
import requests

url = "https://api.cloudraker.com/process/id"

payload = {}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.get(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.cloudraker.com/process/id';
const options = {
  method: 'GET',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.cloudraker.com/process/id"

	payload := strings.NewReader("{}")

	req, _ := http.NewRequest("GET", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.cloudraker.com/process/id")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.cloudraker.com/process/id")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.cloudraker.com/process/id', [
  'body' => '{}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.cloudraker.com/process/id");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.cloudraker.com/process/id")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Query parameters

* **`include`** — comma-separated list of `content`, `results`, `evidence`. These heavier payloads are inlined only when you ask for them.
* **`format`** — `json` (default) or `markdown`.

### `ProcessStatus` response

```jsonc
{
  "processingId": "…",
  "status": "preprocessing | running | done | failed | expired",
  "expiresAt": "…",
  "files": [
    { "fileId": "…", "fileName": "invoice.pdf", "processingKind": null,
      "status": "…", "error": null, "content": null }
  ],
  "actions": [
    { "runId": null, "installedActionId": "…", "status": "…", "error": null, "result": null }
  ]
}
```

`content` (per file), `results` (per action), and `evidence` appear only when requested via `include`.

Audio files have no markdown byproduct. `include=content&format=markdown` returns `null` for audio content — use `format=json`.

### Poll responses

| Status | Body                    | Meaning                                                               |
| ------ | ----------------------- | --------------------------------------------------------------------- |
| `200`  | `ProcessStatus`         | Found — keep polling until `status` is `done`, `failed`, or `expired` |
| `404`  | `{"error":"not_found"}` | Unknown id, or hard-wiped after the grace window                      |
| `410`  | `{"error":"expired"}`   | Purged, still inside the post-TTL grace window                        |

## Purge early

### Request

DELETE [https://api.cloudraker.com/process/\{id}](https://api.cloudraker.com/process/\{id})

```curl
curl -X DELETE https://api.cloudraker.com/process/id \
     -H "Authorization: Bearer <token>"
```

```python
import requests

url = "https://api.cloudraker.com/process/id"

headers = {"Authorization": "Bearer <token>"}

response = requests.delete(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.cloudraker.com/process/id';
const options = {method: 'DELETE', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.cloudraker.com/process/id"

	req, _ := http.NewRequest("DELETE", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.cloudraker.com/process/id")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Delete.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.delete("https://api.cloudraker.com/process/id")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('DELETE', 'https://api.cloudraker.com/process/id', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.cloudraker.com/process/id");
var request = new RestRequest(Method.DELETE);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.cloudraker.com/process/id")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "DELETE"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

Immediately cancels in-flight work and deletes files, runs, and stored results. Returns `204`.

## TTL and lifecycle

A pipeline lives until `expiresAt` (`durationSeconds` from creation; default 24h, max 7 days). At expiry, CloudRaker cancels runs, purges outputs, deletes the files, emits a `processing.expired` webhook, and then serves the id as **`410`** for a short grace window (\~24h) before hard-wiping it to **`404`**. Fetch everything you need before `expiresAt`, or extend the TTL at creation.

## Where to go next

#### [Webhooks](/developers/webhooks)

Verify the signed events `/process` emits, instead of polling.

#### [Files and uploads](/developers/files-and-uploads)

The space-scoped presigned upload flow for large files and persistent storage.