---
title: Add file
description: Add a file, or replace the current file at the same logical path.
doc_version: 0.1.0-preview
last_updated: 2026-09-14
---

# Add file

`POST /namespaces/{namespace}/collections/{collection}/files`

Add a file, or replace the current file at the same logical path.

**Permission:** Cluster key, or a scoped key with write on this collection.

[API key types and authentication](/engine/reference/authentication.md).

Send Idempotency-Key. Retry the same logical request with the same key and body. Idempotency records remain available for replay for 24 hours.

URI and inline sources return 202 with a file and job. An upload source returns 201 with a file, job, and upload URL.

For upload, PUT bytes to upload.url before upload.expires_at. Do not send your Engine key to the upload host.

Adding a URI copies its current bytes. A later change in the object store does not change the index. Add the same path again to replace its content.

A replacement becomes searchable when processing succeeds. Inspect the new file’s job for failures.

Use events webhooks for live status. Read the job to inspect its current state or recover after a missed event.

## Example request

```http
POST /namespaces/acme/collections/incident-logs/files HTTP/1.1
Host: engine.example.com
Authorization: Bearer <GRAPHON_ENGINE_API_KEY>
Content-Type: application/json
Idempotency-Key: 8f3c1e2a-4b5d-6e7f-8091-a2b3c4d5e6f7

{
  "path": "/reports/incident.txt",
  "source": {
    "type": "inline",
    "content_type": "text/plain",
    "text": "Checkout requests stalled because the database connection pool was exhausted."
  },
  "metadata": {
    "environment": "production"
  }
}
```

## Path parameters

### `namespace`

**string · required**

Namespace name or ns_ ID. It must be visible to this key.

### `collection`

**string · required**

Collection name or col_ ID within the namespace. It must be visible to this key.

## Headers

### `Idempotency-Key`

**string · required**

Unique key for this logical operation. Reuse it only with the same request for retries. Retained for 86,400 seconds. A changed body returns idempotency_conflict.

## Request body

### `metadata`

**map<string, JSON value> · optional**

Application data attached to the file, as a JSON object. Use metadata.<key> in filters. Values may be any JSON value.

### `path`

**string · required**

Case-sensitive path within the collection, such as /reports/incident.txt. Start with / and omit a trailing /. Maximum 1,024 UTF-8 bytes. Empty, dot, parent, and control-character segments are invalid.

### `source`

**object · required**

Where the file bytes come from. Choose the uri, upload, or inline variant. Each variant has its own required fields.

#### uri

##### `source.bytes`

**integer · optional · nullable**

Declared file size in bytes. Must be a non-negative integer. A declared size is checked against the source.

- Minimum: 0

##### `source.checksum`

**string · optional · nullable**

Optional source checksum: sha256:, md5:, or crc32c: followed by lowercase hexadecimal digits. A mismatch fails processing with source_mismatch.

##### `source.content_type`

**string · required**

MIME type of the file, such as text/plain, application/pdf, or video/mp4.

##### `source.type`

**"uri" · required**

Use uri to index an object that the cluster can read through a configured storage connector.

##### `source.uri`

**string · required**

Readable gs:// or graphon:// storage URI. Check GET /info for supported schemes. Adding the file copies its current content. Later source changes require another add.

#### upload

##### `source.bytes`

**integer · optional · nullable**

Declared file size in bytes. Must be a non-negative integer. A declared size is checked against the source.

- Minimum: 0

##### `source.content_type`

**string · required**

MIME type of the file, such as text/plain, application/pdf, or video/mp4.

##### `source.type`

**"upload" · required**

Use upload to receive a URL for a separate PUT of the file bytes.

#### inline

##### `source.content_type`

**string · required**

MIME type of the file, such as text/plain, application/pdf, or video/mp4.

##### `source.text`

**string · required**

UTF-8 text to index. This field is write-only and never appears in file responses. Maximum 1,048,576 bytes, subject to the same limit for the entire JSON request.

##### `source.type`

**"inline" · required**

Use inline to submit a small text file directly in this JSON request.

## Responses

### 202 · File accepted

An inline or URI file was accepted for indexing. The file becomes searchable after processing succeeds.

Content type: `application/json`.

#### Response headers

##### `x-request-id`

**string · required**

Request correlation ID. Retain it when investigating a failed or unexpected request.

```json
{
  "file": {
    "id": "fil_01K6Q6N7D8E9F0G1H2J3K4M5N6",
    "path": "/reports/incident.txt",
    "status": "indexing",
    "source": {
      "type": "inline",
      "content_type": "text/plain",
      "bytes": 77
    },
    "metadata": {
      "environment": "production"
    },
    "created_at": "2026-10-06T10:00:00Z",
    "updated_at": "2026-10-06T10:00:00Z"
  },
  "job": {
    "id": "job_01K6Q6N7D8E9F0G1H2J3K4M5N6",
    "type": "index_file",
    "status": "running",
    "namespace": "acme",
    "collection": "incident-logs",
    "file_id": "fil_01K6Q6N7D8E9F0G1H2J3K4M5N6",
    "error": null,
    "created_at": "2026-10-06T10:00:00Z",
    "updated_at": "2026-10-06T10:00:00Z"
  }
}
```

#### Response fields

##### `file`

**object · required**

File accepted for processing. Inspect its status before retrieval.

###### `file.created_at`

**string (date-time) · required**

Time the resource was created, as an ISO 8601 UTC timestamp.

###### `file.error`

**object · optional · nullable**

Processing failure details. Omitted when the file has no error.

###### `file.error.code`

**string · required**

Machine-readable reason for the failure. Use this value for program logic and message for display or diagnostics.

###### `file.error.message`

**string · required**

Human-readable explanation of the failure. Do not parse this text to decide how to retry.

###### `file.governing_rule`

**object · optional · nullable**

Rule that governs retrieval of this file. Present only when the collection enforces access rules.

###### `file.governing_rule.file`

**string · optional · nullable**

Exact file target of the governing rule. Omitted for a folder rule.

###### `file.governing_rule.folder`

**string · optional · nullable**

Folder prefix of the governing rule. Omitted for a file rule.

###### `file.governing_rule.id`

**string · required**

Immutable ID of the rule that governs this file.

###### `file.id`

**string · required**

Immutable file ID with the fil_ prefix. Retain this ID from the add response.

###### `file.metadata`

**map<string, JSON value> · required**

Application data attached to the file, as a JSON object. Use metadata.<key> in filters. Values may be any JSON value.

###### `file.path`

**string · required**

Case-sensitive path within the collection, such as /reports/incident.txt. Start with / and omit a trailing /. Maximum 1,024 UTF-8 bytes. Empty, dot, parent, and control-character segments are invalid.

###### `file.source`

**object · required**

Source metadata. Inline text is never returned. Its bytes field is the UTF-8 length of the submitted text.

###### `file.source.bytes`

**integer · optional · nullable**

Declared size for URI and upload sources, or measured UTF-8 size for inline text. Omitted when no size is known.

###### `file.source.checksum`

**string · optional · nullable**

Optional source checksum: sha256:, md5:, or crc32c: followed by lowercase hexadecimal digits. A mismatch fails processing with source_mismatch.

###### `file.source.content_type`

**string · required**

MIME type of the file, such as text/plain, application/pdf, or video/mp4.

###### `file.source.type`

**string · required**

How the file was supplied: URI pointer, upload, or inline text.

- `uri`: A storage URI source.
- `upload`: A separately uploaded source.
- `inline`: Text submitted in the add request.

###### `file.source.uri`

**string · optional · nullable**

Original storage URI. Present for URI sources. It is distinct from the file’s logical path.

###### `file.status`

**string · required**

Current processing state. Only ready files contribute new indexed content to retrieval.

- `pending`: Waiting for bytes or processing.
- `indexing`: Building searchable content.
- `ready`: Available for retrieval.
- `failed`: Processing failed. Inspect error.
- `deleting`: Removal and cleanup are in progress.

###### `file.updated_at`

**string (date-time) · required**

Time the resource last changed, as an ISO 8601 UTC timestamp.

##### `job`

**object · required**

Durable index_file job for this add or replacement.

###### `job.collection`

**string · optional · nullable**

Collection name when this job belongs to a collection. Otherwise omitted.

###### `job.created_at`

**string (date-time) · required**

Time the resource was created, as an ISO 8601 UTC timestamp.

###### `job.error`

**object · required · nullable**

Failure details, or null when no failure is recorded.

###### `job.error.code`

**string · required**

Machine-readable reason for the failure. Use this value for program logic and message for display or diagnostics.

###### `job.error.message`

**string · required**

Human-readable explanation of the failure. Do not parse this text to decide how to retry.

###### `job.file_id`

**string · optional · nullable**

File ID when this job acts on one file. Otherwise omitted.

###### `job.id`

**string · required**

Immutable job ID with the job_ prefix. Use it to read the current state.

###### `job.namespace`

**string · optional · nullable**

Namespace name when this job belongs to a namespace.

###### `job.status`

**string · required**

Current state of the asynchronous work. succeeded, failed, and canceled are terminal states.

- `queued`: Accepted and waiting to start.
- `running`: Work is in progress.
- `succeeded`: Work completed successfully.
- `failed`: Work ended with an error.
- `canceled`: Work ended after cancellation.

###### `job.type`

**string · required**

Kind of work accepted by the API.

- `index_file`: Index a new or replacement file.
- `delete_file`: Remove a file and clean its derived data.
- `reindex_collection`: Rebuild the collection’s derived data.
- `delete_collection`: Remove a collection and its derived data.
- `delete_namespace`: Remove a namespace and its collections.
- `apply_access_rule`: Apply changed governing rules to affected files.

###### `job.updated_at`

**string (date-time) · required**

Time the resource last changed, as an ISO 8601 UTC timestamp.

##### `upload`

**object · optional · nullable**

Present only for upload sources. PUT the file bytes to its URL before its expiry time.

###### `upload.expires_at`

**string (date-time) · required**

ISO 8601 UTC deadline for completing the upload. A missing upload eventually fails with upload_expired.

###### `upload.url`

**string · required**

Signed upload URL. PUT the file bytes to this URL to complete the upload. Do not send the Engine API key to this URL.

### 201 · Upload created

The upload variant returns a signed URL. PUT the file bytes there before expires_at.

#### Request for this response

```http
POST /namespaces/acme/collections/incident-logs/files HTTP/1.1
Host: engine.example.com
Authorization: Bearer <GRAPHON_ENGINE_API_KEY>
Content-Type: application/json
Idempotency-Key: 8f3c1e2a-4b5d-6e7f-8091-a2b3c4d5e6f7

{
  "path": "/reports/incident.txt",
  "source": {
    "type": "upload",
    "content_type": "text/plain",
    "bytes": 77
  },
  "metadata": {
    "environment": "production"
  }
}
```

Content type: `application/json`.

#### Response headers

##### `x-request-id`

**string · required**

Request correlation ID. Retain it when investigating a failed or unexpected request.

##### `Location`

**string · required**

The path of the new resource, by ids.

```json
{
  "file": {
    "id": "fil_01K6Q6N7D8E9F0G1H2J3K4M5N6",
    "path": "/reports/incident.txt",
    "status": "pending",
    "source": {
      "type": "upload",
      "content_type": "text/plain",
      "bytes": 77
    },
    "metadata": {
      "environment": "production"
    },
    "created_at": "2026-10-06T10:00:00Z",
    "updated_at": "2026-10-06T10:00:00Z"
  },
  "job": {
    "id": "job_01K6Q6N7D8E9F0G1H2J3K4M5N6",
    "type": "index_file",
    "status": "running",
    "namespace": "acme",
    "collection": "incident-logs",
    "file_id": "fil_01K6Q6N7D8E9F0G1H2J3K4M5N6",
    "error": null,
    "created_at": "2026-10-06T10:00:00Z",
    "updated_at": "2026-10-06T10:00:00Z"
  },
  "upload": {
    "url": "https://storage.example.com/upload/incident?signature=example",
    "expires_at": "2026-10-06T11:00:00Z"
  }
}
```

#### Response fields

##### `file`

**object · required**

File accepted for processing. Inspect its status before retrieval.

###### `file.created_at`

**string (date-time) · required**

Time the resource was created, as an ISO 8601 UTC timestamp.

###### `file.error`

**object · optional · nullable**

Processing failure details. Omitted when the file has no error.

###### `file.error.code`

**string · required**

Machine-readable reason for the failure. Use this value for program logic and message for display or diagnostics.

###### `file.error.message`

**string · required**

Human-readable explanation of the failure. Do not parse this text to decide how to retry.

###### `file.governing_rule`

**object · optional · nullable**

Rule that governs retrieval of this file. Present only when the collection enforces access rules.

###### `file.governing_rule.file`

**string · optional · nullable**

Exact file target of the governing rule. Omitted for a folder rule.

###### `file.governing_rule.folder`

**string · optional · nullable**

Folder prefix of the governing rule. Omitted for a file rule.

###### `file.governing_rule.id`

**string · required**

Immutable ID of the rule that governs this file.

###### `file.id`

**string · required**

Immutable file ID with the fil_ prefix. Retain this ID from the add response.

###### `file.metadata`

**map<string, JSON value> · required**

Application data attached to the file, as a JSON object. Use metadata.<key> in filters. Values may be any JSON value.

###### `file.path`

**string · required**

Case-sensitive path within the collection, such as /reports/incident.txt. Start with / and omit a trailing /. Maximum 1,024 UTF-8 bytes. Empty, dot, parent, and control-character segments are invalid.

###### `file.source`

**object · required**

Source metadata. Inline text is never returned. Its bytes field is the UTF-8 length of the submitted text.

###### `file.source.bytes`

**integer · optional · nullable**

Declared size for URI and upload sources, or measured UTF-8 size for inline text. Omitted when no size is known.

###### `file.source.checksum`

**string · optional · nullable**

Optional source checksum: sha256:, md5:, or crc32c: followed by lowercase hexadecimal digits. A mismatch fails processing with source_mismatch.

###### `file.source.content_type`

**string · required**

MIME type of the file, such as text/plain, application/pdf, or video/mp4.

###### `file.source.type`

**string · required**

How the file was supplied: URI pointer, upload, or inline text.

- `uri`: A storage URI source.
- `upload`: A separately uploaded source.
- `inline`: Text submitted in the add request.

###### `file.source.uri`

**string · optional · nullable**

Original storage URI. Present for URI sources. It is distinct from the file’s logical path.

###### `file.status`

**string · required**

Current processing state. Only ready files contribute new indexed content to retrieval.

- `pending`: Waiting for bytes or processing.
- `indexing`: Building searchable content.
- `ready`: Available for retrieval.
- `failed`: Processing failed. Inspect error.
- `deleting`: Removal and cleanup are in progress.

###### `file.updated_at`

**string (date-time) · required**

Time the resource last changed, as an ISO 8601 UTC timestamp.

##### `job`

**object · required**

Durable index_file job for this add or replacement.

###### `job.collection`

**string · optional · nullable**

Collection name when this job belongs to a collection. Otherwise omitted.

###### `job.created_at`

**string (date-time) · required**

Time the resource was created, as an ISO 8601 UTC timestamp.

###### `job.error`

**object · required · nullable**

Failure details, or null when no failure is recorded.

###### `job.error.code`

**string · required**

Machine-readable reason for the failure. Use this value for program logic and message for display or diagnostics.

###### `job.error.message`

**string · required**

Human-readable explanation of the failure. Do not parse this text to decide how to retry.

###### `job.file_id`

**string · optional · nullable**

File ID when this job acts on one file. Otherwise omitted.

###### `job.id`

**string · required**

Immutable job ID with the job_ prefix. Use it to read the current state.

###### `job.namespace`

**string · optional · nullable**

Namespace name when this job belongs to a namespace.

###### `job.status`

**string · required**

Current state of the asynchronous work. succeeded, failed, and canceled are terminal states.

- `queued`: Accepted and waiting to start.
- `running`: Work is in progress.
- `succeeded`: Work completed successfully.
- `failed`: Work ended with an error.
- `canceled`: Work ended after cancellation.

###### `job.type`

**string · required**

Kind of work accepted by the API.

- `index_file`: Index a new or replacement file.
- `delete_file`: Remove a file and clean its derived data.
- `reindex_collection`: Rebuild the collection’s derived data.
- `delete_collection`: Remove a collection and its derived data.
- `delete_namespace`: Remove a namespace and its collections.
- `apply_access_rule`: Apply changed governing rules to affected files.

###### `job.updated_at`

**string (date-time) · required**

Time the resource last changed, as an ISO 8601 UTC timestamp.

##### `upload`

**object · optional · nullable**

Present only for upload sources. PUT the file bytes to its URL before its expiry time.

###### `upload.expires_at`

**string (date-time) · required**

ISO 8601 UTC deadline for completing the upload. A missing upload eventually fails with upload_expired.

###### `upload.url`

**string · required**

Signed upload URL. PUT the file bytes to this URL to complete the upload. Do not send the Engine API key to this URL.

## Errors

[Error format and retry guidance](/engine/reference/errors.md).

### 400 · `invalid_request`

A field, path, parameter, or conditional request rule is invalid.

Correct the request. Unknown fields are rejected.

```json
{
  "error": {
    "code": "invalid_request",
    "message": "A field, path, parameter, or conditional request rule is invalid.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 400 · `invalid_uri`

The source URI uses an unsupported scheme or an invalid location.

Check GET /info for supported schemes and correct the URI.

```json
{
  "error": {
    "code": "invalid_uri",
    "message": "The source URI uses an unsupported scheme or an invalid location.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 401 · `unauthenticated`

The API key is missing, invalid, or revoked.

Send an active key in Authorization: Bearer or X-API-Key.

```json
{
  "error": {
    "code": "unauthenticated",
    "message": "The API key is missing, invalid, or revoked.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 403 · `forbidden`

The key can see the resource but lacks permission for this operation.

Use a key with the required action or scope kind.

```json
{
  "error": {
    "code": "forbidden",
    "message": "The key can see the resource but lacks permission for this operation.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 404 · `not_found`

The resource is absent, deleted, or outside this key’s scope.

Check the identifier and the key’s namespace and collection scope.

```json
{
  "error": {
    "code": "not_found",
    "message": "The resource is absent, deleted, or outside this key’s scope.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 409 · `idempotency_conflict`

This idempotency key was already used with a different request body.

Reuse the original body for a retry, or choose a new key for a new operation.

```json
{
  "error": {
    "code": "idempotency_conflict",
    "message": "This idempotency key was already used with a different request body.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 409 · `operation_in_progress`

An unfinished operation conflicts with this request.

Inspect the existing job and wait for its completion event before retrying.

```json
{
  "error": {
    "code": "operation_in_progress",
    "message": "An unfinished operation conflicts with this request.",
    "request_id": "req_example",
    "retryable": true,
    "details": {}
  }
}
```

### 409 · `limit_exceeded`

The operation would exceed a documented size or count limit.

Reduce the input or resource count. This is not a tenant billing limit.

```json
{
  "error": {
    "code": "limit_exceeded",
    "message": "The operation would exceed a documented size or count limit.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 413 · `request_too_large`

The JSON request body exceeds 1,048,576 bytes.

Reduce the JSON body. Use upload or a URI source for larger file content.

```json
{
  "error": {
    "code": "request_too_large",
    "message": "The JSON request body exceeds 1,048,576 bytes.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 422 · `storage_unreachable`

The cluster cannot read the source object.

Check that the object exists and the cluster’s connector can read it, then retry.

```json
{
  "error": {
    "code": "storage_unreachable",
    "message": "The cluster cannot read the source object.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 503 · `temporarily_unavailable`

A dependency, cluster readiness condition, or capacity limit prevents this operation.

Inspect retryable and details. Retry only when permitted, preserving the idempotency key for the same write.

```json
{
  "error": {
    "code": "temporarily_unavailable",
    "message": "A dependency, cluster readiness condition, or capacity limit prevents this operation.",
    "request_id": "req_example",
    "retryable": true,
    "details": {}
  }
}
```

### 500 · `internal_error`

An unexpected server failure prevented this request from completing.

Retain x-request-id for investigation. Follow retryable before retrying a write.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected server failure prevented this request from completing.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

## Other request languages

### cURL

```bash
curl -sS -X POST "$GRAPHON_ENGINE_URL/namespaces/acme/collections/incident-logs/files" \
  -H "Authorization: Bearer $GRAPHON_ENGINE_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 8f3c1e2a-4b5d-6e7f-8091-a2b3c4d5e6f7' \
  --data-raw '{
  "path": "/reports/incident.txt",
  "source": {
    "type": "inline",
    "content_type": "text/plain",
    "text": "Checkout requests stalled because the database connection pool was exhausted."
  },
  "metadata": {
    "environment": "production"
  }
}'
```

### Python

```python
import json
import os
import urllib.request

request = urllib.request.Request(
    os.environ["GRAPHON_ENGINE_URL"] + "/namespaces/acme/collections/incident-logs/files",
    data=json.dumps({"path": "/reports/incident.txt", "source": {"type": "inline", "content_type": "text/plain", "text": "Checkout requests stalled because the database connection pool was exhausted."}, "metadata": {"environment": "production"}}).encode("utf-8"),
    headers={
        "Authorization": "Bearer " + os.environ["GRAPHON_ENGINE_API_KEY"],
        "Content-Type": "application/json",
        "Idempotency-Key": "8f3c1e2a-4b5d-6e7f-8091-a2b3c4d5e6f7",
    },
    method="POST",
)
with urllib.request.urlopen(request) as response:
    print(response.read().decode())
```

### JavaScript

```javascript
const response = await fetch(process.env.GRAPHON_ENGINE_URL + "/namespaces/acme/collections/incident-logs/files", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GRAPHON_ENGINE_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "8f3c1e2a-4b5d-6e7f-8091-a2b3c4d5e6f7",
  },
  body: JSON.stringify({"path":"/reports/incident.txt","source":{"type":"inline","content_type":"text/plain","text":"Checkout requests stalled because the database connection pool was exhausted."},"metadata":{"environment":"production"}}),
});
console.log(response.status, await response.text());
```

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
