Drop-offs API

Drop-offs are secure, schema-validated, point-to-point data handoffs between agents. They implement a custody-transfer model: a producing agent creates a drop-off with a JSON Schema, deposits a payload that matches the schema, and a designated consuming agent collects it. Payloads are encrypted with KMS at rest and automatically deleted after collection or expiry. Drop-off IDs are prefixed with drp_.

All endpoints require an Authorization: Bearer sk_live_... header with a valid operator API key or team member API key.

Drop-off Lifecycle

Every drop-off follows a strict state machine:

  • created — Drop-off location established with schema, producer, and consumer agents. Waiting for data deposit.
  • deposited — Payload has been deposited and validated against the schema. Waiting for consumer to collect.
  • collected — Consumer has collected the payload. Encrypted data is wiped from storage.
  • expired — TTL elapsed before collection. Data is wiped by the background worker.
  • failed — Terminal failure state.

Transitions are one-way. There is no way to revert a state or reuse a drop-off after collection or expiry.

Create Drop-off

POST /v1/dropoffs

Create a new drop-off location with a JSON Schema, producing agent, and consuming agent. The schema is used to validate the payload at deposit time — any data that does not conform will be rejected.

Request Body

json
{
  "from_agent": "agt_7kx9m2nq4p",
  "to_agent": "agt_3fh8j1kw6r",
  "schema": {
    "type": "object",
    "properties": {
      "invoice_id": { "type": "string" },
      "amount": { "type": "number", "minimum": 0 },
      "currency": { "type": "string", "enum": ["USD", "EUR", "SEK"] }
    },
    "required": ["invoice_id", "amount", "currency"]
  },
  "ttl_seconds": 1800,
  "on_expire": "notify"
}
  • from_agent (string, required) — Agent ID of the producer who will deposit data.
  • to_agent (string, required) — Agent ID of the consumer who is authorized to collect.
  • schema (object, required) — JSON Schema defining the expected payload shape. Validated with Ajv.
  • ttl_seconds (number, optional) — Time-to-live in seconds. Min: 60, max: 86400 (24 hours). Default: 1800 (30 minutes).
  • on_expire (string, optional) — Action when TTL expires: "notify", "retry", or "fail". Default: "notify".

Request Example

bash
curl -X POST https://api.getstack.run/v1/dropoffs \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk_live_op_abc123" \
  -d '{
    "from_agent": "agt_7kx9m2nq4p",
    "to_agent": "agt_3fh8j1kw6r",
    "schema": {
      "type": "object",
      "properties": {
        "invoice_id": { "type": "string" },
        "amount": { "type": "number" }
      },
      "required": ["invoice_id", "amount"]
    },
    "ttl_seconds": 1800
  }'

Response — 201 Created

json
{
  "id": "drp_9xm3kR7wL2",
  "operator_id": "op_abc123",
  "from_agent_id": "agt_7kx9m2nq4p",
  "to_agent_id": "agt_3fh8j1kw6r",
  "status": "created",
  "schema": {
    "type": "object",
    "properties": {
      "invoice_id": { "type": "string" },
      "amount": { "type": "number" }
    },
    "required": ["invoice_id", "amount"]
  },
  "on_expire": "notify",
  "payload_hash": null,
  "created_at": "2026-04-15T10:30:00.000Z",
  "deposited_at": null,
  "collected_at": null,
  "expires_at": "2026-04-15T11:00:00.000Z"
}

Error Responses

  • 400 Bad Request — Invalid JSON Schema, ttl_seconds out of range, or missing required fields.
  • 401 Unauthorized — Missing or invalid Authorization header.
  • 404 Not Found — from_agent or to_agent does not exist.
  • 429 Too Many Requests — Drop-off creation rate limit exceeded for your tier.

Deposit Payload

POST /v1/dropoffs/:id/deposit

Deposit a payload into a created drop-off. The payload is validated against the drop-off's JSON Schema using Ajv. If validation fails, the deposit is rejected with detailed errors. The payload is encrypted with KMS before storage.

Request Body

json
{
  "agent_id": "agt_7kx9m2nq4p",
  "payload": {
    "invoice_id": "INV-2026-0042",
    "amount": 1250.00,
    "currency": "USD"
  }
}
  • agent_id (string, required) — The agent performing the deposit. Must match the from_agent on the drop-off.
  • payload (object, required) — The data to deposit. Must conform to the drop-off schema.

Request Example

bash
curl -X POST https://api.getstack.run/v1/dropoffs/drp_9xm3kR7wL2/deposit \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk_live_op_abc123" \
  -d '{
    "agent_id": "agt_7kx9m2nq4p",
    "payload": {
      "invoice_id": "INV-2026-0042",
      "amount": 1250.00,
      "currency": "USD"
    }
  }'

Error Responses

  • 400 Bad Request — Payload does not match schema, or drop-off is not in "created" status.
  • 401 Unauthorized — Missing or invalid Authorization header.
  • 403 Forbidden — agent_id does not match the from_agent on the drop-off.
  • 404 Not Found — Drop-off does not exist.

Schema validation is a hard gate. There is no way to bypass it or deposit data that does not conform to the declared schema. This ensures data integrity across the producer-consumer boundary.

Collect Payload

POST /v1/dropoffs/:id/collect

Collect the payload from a deposited drop-off. Only the designated consumer agent (the to_agent) can collect. After successful collection, the encrypted payload is permanently wiped from storage.

Request Body

json
{
  "agent_id": "agt_3fh8j1kw6r"
}
  • agent_id (string, required) — The agent performing the collection. Must match the to_agent on the drop-off.

Request Example

bash
curl -X POST https://api.getstack.run/v1/dropoffs/drp_9xm3kR7wL2/collect \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk_live_op_abc123" \
  -d '{
    "agent_id": "agt_3fh8j1kw6r"
  }'

Response — 200 OK

json
{
  "payload": {
    "invoice_id": "INV-2026-0042",
    "amount": 1250.00,
    "currency": "USD"
  }
}

Error Responses

  • 401 Unauthorized — Missing or invalid Authorization header.
  • 403 Forbidden — agent_id does not match the to_agent on the drop-off.
  • 404 Not Found — Drop-off does not exist.
  • 409 Conflict — Payload has already been collected.
  • 410 Gone — Drop-off has expired and data has been wiped.

Collection is a one-time operation. Once data is collected, it is permanently deleted from STACK's storage. The consumer receives the payload in the response and is responsible for handling it from that point forward.

Get Drop-off Status

GET /v1/dropoffs/:id

Retrieve the current status and metadata for a drop-off. This never returns the actual payload — only the schema, status, timestamps, and related IDs.

Request Example

bash
curl https://api.getstack.run/v1/dropoffs/drp_9xm3kR7wL2 \
  -H "Authorization: Bearer sk_live_op_abc123"

List Drop-offs

GET /v1/dropoffs

List all drop-offs for your operator account.

Request Example

bash
curl https://api.getstack.run/v1/dropoffs \
  -H "Authorization: Bearer sk_live_op_abc123"

Manually Expire Drop-off

POST /v1/dropoffs/:id/expire

Manually expire a drop-off before its TTL elapses. This transitions the drop-off to the expired state and wipes any deposited payload data. Useful for cancelling a drop-off that is no longer needed.

Request Example

bash
curl -X POST https://api.getstack.run/v1/dropoffs/drp_9xm3kR7wL2/expire \
  -H "Authorization: Bearer sk_live_op_abc123"

TTL and Automatic Expiry

Drop-offs are ephemeral by design. A background worker process monitors active drop-offs and transitions expired ones to the expired state. When a drop-off expires:

  • Status transitions to "expired" regardless of current state (created or deposited).
  • Encrypted payload is permanently deleted from storage.
  • The drop-off metadata (schema, timestamps, IDs) is preserved for audit purposes.
  • The on_expire action is triggered (notify, retry, or fail).

Tier Limits

Drop-off usage is metered monthly:

  • Free — 100 drop-offs/month
  • Developer ($9.99/mo) — 5,000 drop-offs/month
  • Studio ($99/mo) — 50,000 drop-offs/month
  • Enterprise — Unlimited
STACK — Infrastructure for AI Agents