---
title: Add a file
description: Add and replace files using inline text, uploads, and storage URIs.
doc_version: 0.1.0-preview
last_updated: 2026-09-14
---

# Add a file

Add content from inline text, an upload, or a supported storage URI. Track processing, replace content at the same path, and recover from failures.

## Before you begin

Create a namespace and collection first. The examples use `acme` and `incident-logs`. Your key needs `write` within that collection. Configure [events](/engine/guides/jobs-and-events) to receive processing status. The [quickstart](/engine/get-started) creates these resources.

*Illustration: All source types create a file and a job. An upload also requires a PUT. Wait for a ready event before relying on the file in retrieval.*

## Add inline text

Use `inline` for a small text file. `path` names the file inside the collection, `content_type` declares its media type, and `text` contains its UTF-8 content. `metadata` stores your application’s additional facts.

**POST https://engine.example.com/namespaces/acme/collections/incident-logs/files**

```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: checkout-text-version-1

{
  "path": "/incidents/checkout.txt",
  "source": {
    "type": "inline",
    "content_type": "text/plain",
    "text": "Checkout stalled because the service exhausted its connection pool. Increase the pool size before the next deployment."
  },
  "metadata": {
    "environment": "production",
    "service": "checkout"
  }
}
```

**202 Accepted**

```json
{
  "file": {
    "id": "fil_01J8Z3K4N5P6Q7R8S9T0V1W2X3",
    "path": "/incidents/checkout.txt",
    "status": "indexing",
    "source": {
      "type": "inline",
      "content_type": "text/plain",
      "bytes": 118
    },
    "metadata": {
      "environment": "production",
      "service": "checkout"
    },
    "created_at": "2026-10-06T12:00:00Z",
    "updated_at": "2026-10-06T12:00:00Z"
  },
  "job": {
    "id": "job_01J8Z3K4N5P6Q7R8S9T0V1W2X3",
    "type": "index_file",
    "status": "running",
    "namespace": "acme",
    "collection": "incident-logs",
    "file_id": "fil_01J8Z3K4N5P6Q7R8S9T0V1W2X3",
    "error": null,
    "created_at": "2026-10-06T12:00:00Z",
    "updated_at": "2026-10-06T12:00:00Z"
  }
}
```

The response includes `source.bytes`, not `source.text`. Save `file.id` and `job.id`. The entire JSON body, including escaped text and metadata, must fit within 1,048,576 bytes.

## Upload file bytes

Use `upload` when you have bytes rather than a URI the cluster can read. Engine returns an upload destination and expiry time. Upload the bytes before that time.

**POST https://engine.example.com/namespaces/acme/collections/incident-logs/files**

```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: incident-notes-upload-1

{
  "path": "/incidents/notes.txt",
  "source": {
    "type": "upload",
    "content_type": "text/plain",
    "bytes": 15
  }
}
```

**201 Created**

```json
{
  "file": {
    "id": "fil_01J8Z3K4N5P6Q7R8S9T0V1W2X3",
    "path": "/incidents/notes.txt",
    "status": "pending",
    "source": {
      "type": "upload",
      "content_type": "text/plain",
      "bytes": 15
    },
    "metadata": {},
    "created_at": "2026-10-06T12:00:00Z",
    "updated_at": "2026-10-06T12:00:00Z"
  },
  "job": {
    "id": "job_01J8Z3K4N5P6Q7R8S9T0V1W2X3",
    "type": "index_file",
    "status": "queued",
    "namespace": "acme",
    "collection": "incident-logs",
    "file_id": "fil_01J8Z3K4N5P6Q7R8S9T0V1W2X3",
    "error": null,
    "created_at": "2026-10-06T12:00:00Z",
    "updated_at": "2026-10-06T12:00:00Z"
  },
  "upload": {
    "url": "https://upload.example.com/signed-destination",
    "expires_at": "2026-10-06T12:15:00Z"
  }
}
```

Use the actual `upload.url` returned by Engine, including its query string. The URL authorizes the upload. It is a different destination from the Engine API.

**PUT to the returned upload.url · illustrative destination**

```http
PUT /signed-destination HTTP/1.1
Host: upload.example.com
Content-Type: text/plain

Incident notes.
```

A successful PUT supplies the bytes. Processing then starts. If no upload arrives before `upload.expires_at`, the file fails. Request another upload with a new idempotency key and complete it before the new expiry.

## Add from a storage URI

Call `/info` and check `storage.schemes`. For a supported URI, grant the cluster read access to the object. `bytes` is an optional declared size, and `checksum` can identify the expected source content.

**POST https://engine.example.com/namespaces/acme/collections/incident-logs/files**

```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: checkout-video-version-1

{
  "path": "/incidents/checkout-video.mp4",
  "source": {
    "type": "uri",
    "uri": "gs://acme-ingest/incidents/checkout-video.mp4",
    "content_type": "video/mp4",
    "bytes": 1048576
  },
  "metadata": {
    "service": "checkout"
  }
}
```

The response is `202` with a file and an indexing job, as in the inline example. A later change at the storage URI does not update the indexed content. Submit another add request to index changed bytes.

| Failure | Action |
| --- | --- |
| 400 `invalid_uri` | Check the URI syntax and schemes reported by /info. |
| 422 `storage_unreachable` | Check the object exists and the cluster can read it. |
| 409 `idempotency_conflict` | Use the original body for a retry, or a new key for new content. |

## Replace an existing file

POST the same path with the replacement source. Use a new idempotency key for the new content. Retain the new file ID and job ID returned by this add. The logical path stays the same; the replacement receives a new file ID.

**POST https://engine.example.com/namespaces/acme/collections/incident-logs/files**

```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: checkout-text-version-2

{
  "path": "/incidents/checkout.txt",
  "source": {
    "type": "inline",
    "content_type": "text/plain",
    "text": "The connection pool limit was increased. Checkout recovered after the service restarted."
  },
  "metadata": {
    "environment": "production",
    "service": "checkout"
  }
}
```

Wait for the replacement to become ready. Ordinary replacement does not need a separate reindex request. Reusing the first request’s idempotency key with different content returns `409 idempotency_conflict`.

## Read and remove content

Use the file ID for a detail request, or find it by exact path:

**GET https://engine.example.com/namespaces/acme/collections/incident-logs/files?path=/incidents/checkout.txt**

```http
GET /namespaces/acme/collections/incident-logs/files?path=/incidents/checkout.txt HTTP/1.1
Host: engine.example.com
Authorization: Bearer <GRAPHON_ENGINE_API_KEY>
```

**200 OK**

```json
{
  "items": [
    {
      "id": "fil_01J8Z3K4N5P6Q7R8S9T0V1W2X3",
      "path": "/incidents/checkout.txt",
      "status": "ready",
      "source": {
        "type": "inline",
        "content_type": "text/plain",
        "bytes": 118
      },
      "metadata": {
        "environment": "production",
        "service": "checkout"
      },
      "created_at": "2026-10-06T12:00:00Z",
      "updated_at": "2026-10-06T12:01:00Z"
    }
  ],
  "next_cursor": null
}
```

[Get file content](/engine/reference/get-file-content) returns a short-lived `url` and `expires_at` when the content is available through this API. Fetch that URL through an authorized application flow. An external source can return `409 external_source` instead.

[Delete file](/engine/reference/delete-file) returns a cleanup job. It removes the file from the collection. It does not delete the original object in an external store. See the [Add file reference](/engine/reference/add-file) for all fields and response variants.

Read [Get job](/engine/reference/get-job) to recover current status after a missed event. When your file is ready, follow [Search](/engine/guides/search) to retrieve its evidence.
## Sitemap

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