---
title: Record a run with the API
description: Send an agent run to Prefactor with plain HTTP requests, without an SDK.
editUrl: true
head: []
template: doc
sidebar:
  hidden: false
  attrs: {}
pagefind: true
draft: false
---

You don't need an SDK to send runs to Prefactor. If your agent is written in a language we don't have an SDK for, or you'd rather not add a dependency, you can record a run with five HTTP requests. This guide walks through them with `curl`, so you can follow the same steps from any language.

By the end you'll have an [instance](/platform/concepts/instance) with one [span](/platform/concepts/span) on your agent's [History tab](/web-app/agent/history).

## Before you start

- An agent registered in Prefactor. See [Register an agent](/guides/register-an-agent).
- A deployment token for that agent and the environment you're recording to, in `PREFACTOR_API_TOKEN`. See [Create a deployment token](/guides/create-a-deployment-token). A deployment token already knows its agent and environment, so the requests below don't need to name them.

```bash
export PREFACTOR_API_TOKEN="<your deployment token>"
export PREFACTOR_URL="https://app.prefactorai.com/api/v1"
```

## Record the run

1. Register the instance.

   Registering creates the instance and tells Prefactor which version of your agent is running and which span types it records. The `agent_version` and `agent_schema_version` are found or created by their `external_identifier`, so sending the same values on every run is safe.

   ```bash
   curl -X POST "$PREFACTOR_URL/agent_instance/register" \
     -H "Authorization: Bearer $PREFACTOR_API_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "idempotency_key": "register-run-0001",
       "agent_version": {
         "external_identifier": "1.0.0",
         "name": "Support agent"
       },
       "agent_schema_version": {
         "external_identifier": "support-agent-schema-1",
         "span_type_schemas": [
           {
             "name": "support:llm",
             "params_schema": { "type": "object" },
             "result_schema": { "type": "object" }
           }
         ]
       }
     }'
   ```

   The response's `details.id` is the instance ID. Save it:

   ```bash
   export INSTANCE_ID="<details.id from the response>"
   ```

2. Start the instance.

   ```bash
   curl -X POST "$PREFACTOR_URL/agent_instance/$INSTANCE_ID/start" \
     -H "Authorization: Bearer $PREFACTOR_API_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{ "idempotency_key": "start-run-0001" }'
   ```

   The instance is now Active.

3. Open a span for a step the agent takes.

   `schema_name` must match a span type you declared when you registered. The `payload` is what the step received.

   ```bash
   curl -X POST "$PREFACTOR_URL/agent_spans" \
     -H "Authorization: Bearer $PREFACTOR_API_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "idempotency_key": "span-run-0001-1",
       "details": {
         "agent_instance_id": "'"$INSTANCE_ID"'",
         "schema_name": "support:llm",
         "status": "active",
         "payload": { "prompt": "Where is my invoice?" }
       }
     }'
   ```

   Save the span's `details.id`:

   ```bash
   export SPAN_ID="<details.id from the response>"
   ```

4. Finish the span with what the step returned.

   ```bash
   curl -X POST "$PREFACTOR_URL/agent_spans/$SPAN_ID/finish" \
     -H "Authorization: Bearer $PREFACTOR_API_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "idempotency_key": "span-run-0001-1-finish",
       "status": "complete",
       "result_payload": { "text": "Your invoice is under Billing." }
     }'
   ```

   Use `"status": "failed"` when the step failed.

5. Finish the instance.

   ```bash
   curl -X POST "$PREFACTOR_URL/agent_instance/$INSTANCE_ID/finish" \
     -H "Authorization: Bearer $PREFACTOR_API_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{ "idempotency_key": "finish-run-0001", "status": "complete" }'
   ```

## Verify

Open the agent's [History tab](/web-app/agent/history). The run is at the top with the status Complete. Select **View** to see the `support:llm` span with its payload and result.

## Make it production-ready

- **Idempotency keys.** Send a new, unique `idempotency_key` with every write, such as a UUID, and reuse it only when you retry that same request. A retried request then can't record a step twice.
- **Retries and rate limits.** Retry `429` and `503` responses after the wait in the `Retry-After` header. See [Rate limits](/api/rate-limits) and [HTTP API](/api/http#errors).
- **Real schemas.** Replace `{ "type": "object" }` with JSON schemas for each span type, and add data risk so Prefactor can score runs. See [Activity schema](/platform/concepts/activity-schema).
- **Termination.** When someone terminates a run from the web app, Prefactor keeps accepting spans, but every span create and finish response then carries `"control": {"terminate": true, "reason": "..."}`. Check for it and stop the agent's work. See [Handle instance termination](/sdks/handling-termination).
- **Fewer requests.** Create a span already finished by sending `"status": "complete"` with `result_payload`, `started_at` and `finished_at` in step 3, and skip step 4. To send several writes in one request, use [bulk operations](/api/http#bulk-operations).

## If something goes wrong

- **`401 bad_authtoken`.** The token is wrong, expired, suspended or revoked. Check it on the [Account › API tokens tab](/web-app/account/api-tokens).
- **`400 validation_errors` on a span.** `schema_name` doesn't match a span type in the registered schema, or the payload doesn't match that span type's `params_schema`. The `errors` map names the field.
- **Registering fails without a deployment token.** With an account token, send `agent_id` and `environment_id` in step 1 as well.

## Related

- [API](/api) - what the API covers, and where to find the OpenAPI spec.
- [Platform API operations](/api/platform) - every operation, with its parameters and responses.
- [SDK overview](/sdks) - the SDKs make these requests for you.