Audit logs

Records agent actions, authority, costs, and evidence in an append-only audit trail. $0.50 per 100,000 events + $1 per 1,000 work units + $1 per GB-month. The base URL is https://api.motor.dev/audit_logs, and the OpenAPI 3.1 specification describes each endpoint.

Overview

Keep an append-only record of an agent’s decisions, attempted actions, and reported outcomes. Signed checkpoints let you verify the recorded history. Store supporting files in Artifact storage and reference them as evidence.

How it works

A ledger holds an ordered sequence of events with a shared expiry. Append events as work happens, then query them by actor, action, or other fields. Save checkpoints outside Motor to verify later that the history extends the record you already have. Scoped client tokens let an agent append events for a specific actor and run.

Verification and retention

Cryptographic proofs establish what Motor recorded and in what order. They do not establish that an agent’s claims are true or authorize its actions. Retention is prepaid and does not renew automatically. Extend a ledger before it expires; events added later share the same expiry.

Staging ledgers incur normal charges; Audit logs does not have a test mode.

Pricing

  • $1 per 1,000 work units. A work unit is one successful request: an append batch, a page, a read, a proof, a creation, a seal, an extension, an export's admission, or a token's issuance. An export also uses one for each chunk of up to 1,000 candidates and one for its manifest.
  • $0.50 per 100,000 new events. Duplicates don't incur another append or retention charge; a new request still costs one work unit.
  • $1 per GB-month retained (a decimal GB for 30 days), prepaid in whole days: 1 MiB for each ledger and 8 KiB for each event, until the ledger expires.

For example, 100,000 events in batches of 100, kept 30 days, cost $0.50 to append, $1 for 1,000 requests, $0.8192 for event retention, and $0.002048576 to create and retain the ledger: about $2.32, before reads, evidence, and exports. The same events sent one at a time cost $100.50 before retention, so batch them. Refused requests and identical replays cost nothing. Exports also incur artifact storage write and retention charges for parts and the manifest, plus read and delivery charges when downloaded. There's no free tier, trial, or monthly minimum.

Limits

  • Events: 4,096 bytes each after normalization; 1 to 100 a request; 512 KiB a request.
  • Pages: 100 items, 128 KiB, and 1,000 examined candidates; cursors work 15 minutes.
  • Retention: 1 to 365 days, and extensions up to 365 days ahead.
  • Each ledger: 256 control receipts and 100 retained artifacts.
  • Each team, by default: 100 ledgers, 100,000 retained events, 2 GiB of allocation, 2 running exports, 100 requests a second (a burst of 200), and 1,000 appended events a second (a burst of 2,000). Use multiple ledgers to spread concurrent appends. Ask support@motor.dev to raise a team's limits.

Examples

Record an agent’s refund attempt

Record a support agent’s refund attempt before contacting the payment provider, then append the result as a child event. Use MOTOR_KEY for your secret key. Responses are trimmed; save complete receipts and checkpoints, replace sample IDs, and use the actual action times. Reuse each Idempotency-Key only for retries of the same operation.

curl -i "https://api.motor.dev/audit_logs/ledgers" \
  -H "Authorization: Bearer $MOTOR_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name": "Support refunds", "retention_days": 30}'
HTTP 201

{
  "id": "ald_...",
  "state": "open",
  "retention_days": "30",
  "tree_size": "0"
}
curl -i "https://api.motor.dev/audit_logs/ledgers/ald_.../events" \
  -H "Authorization: Bearer $MOTOR_KEY" \
  -H "Idempotency-Key: refund-42-attempt" \
  -H "Content-Type: application/json" \
  -d '{"events": [{"action": "refund.submit", "actor": {"type": "agent", "id": "support-agent"}, "targets": [{"type": "order", "id": "order_42"}], "client_event_id": "refund-42-attempt", "outcome": "attempted", "occurred_at": "2026-10-19T09:00:00Z", "authority": {"kind": "policy", "reference": "refund-policy", "policy_version": "2026-10"}}]}'
HTTP 201

{
  "receipts": [
    {
      "event_id": "aev_attempt",
      "sequence": "1",
      "duplicate": false
    }
  ],
  "tree_size": "1",
  "checkpoint": {
    "tree_size": "1",
    "key_id": "signing-key-id",
    "signature": "<returned signature>"
  }
}

After the provider confirms the refund, append the result. Replace the evidence URI with the real provider receipt; Motor stores the reference but does not fetch it. If the result is uncertain, record unknown and check the payment provider’s records before retrying the refund.

curl -i "https://api.motor.dev/audit_logs/ledgers/ald_.../events" \
  -H "Authorization: Bearer $MOTOR_KEY" \
  -H "Idempotency-Key: refund-42-success" \
  -H "Content-Type: application/json" \
  -d '{"events": [{"action": "refund.submit", "actor": {"type": "agent", "id": "support-agent"}, "targets": [{"type": "order", "id": "order_42"}], "client_event_id": "refund-42-success", "outcome": "succeeded", "occurred_at": "2026-10-19T09:00:05Z", "parent_event_id": "aev_attempt", "attributes": {"amount": "49.00", "currency": "USD", "provider_receipt_id": "re_42"}, "evidence": [{"uri": "https://payments.example.com/receipts/re_42"}]}]}'
HTTP 201

{
  "receipts": [
    {
      "event_id": "aev_result",
      "sequence": "2",
      "duplicate": false
    }
  ],
  "tree_size": "2",
  "checkpoint": {
    "tree_size": "2",
    "signature": "<returned signature>"
  }
}
curl -i "https://api.motor.dev/audit_logs/ledgers/ald_.../events?target_type=order&target_id=order_42" \
  -H "Authorization: Bearer $MOTOR_KEY"
HTTP 200

{
  "data": [
    {
      "id": "aev_attempt",
      "sequence": "1",
      "event": {
        "action": "refund.submit",
        "outcome": "attempted"
      }
    },
    {
      "id": "aev_result",
      "sequence": "2",
      "parent_event_id": "aev_attempt",
      "event": {
        "action": "refund.submit",
        "outcome": "succeeded",
        "attributes": {
          "amount": "49.00",
          "currency": "USD",
          "provider_receipt_id": "re_42"
        }
      }
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Append with a scoped client token

Let an agent record events for one actor and run in one ledger. Set limits on how many events it can append and how much it can spend. Use the ledger from the first example and set MOTOR_KEY to your secret key; give the agent only the returned client token.

curl -i "https://api.motor.dev/meta/client_tokens" \
  -H "Authorization: Bearer $MOTOR_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"scopes": ["audit_logs:append"], "expires_in": 3600, "audit_logs": {"ledger_id": "ald_...", "actor": {"type": "agent", "id": "support-agent"}, "run_id": "refund-review-42", "max_spend_usd": "0.50", "max_events": 100}}'
HTTP 201

{
  "id": "ct_...",
  "token": "motor_ct_...",
  "scope": "audit_logs:append"
}

Store the returned token as CLIENT_TOKEN. The event’s actor and run_id must match its scope:

curl -i "https://api.motor.dev/audit_logs/ledgers/ald_.../events" \
  -H "Authorization: Bearer $CLIENT_TOKEN" \
  -H "Idempotency-Key: scoped-refund-attempt" \
  -H "Content-Type: application/json" \
  -d '{"events": [{"action": "refund.submit", "actor": {"type": "agent", "id": "support-agent"}, "targets": [{"type": "order", "id": "order_42"}], "client_event_id": "scoped-refund-attempt", "outcome": "attempted", "occurred_at": "2026-10-19T09:00:00Z", "authority": {"kind": "policy", "reference": "refund-policy", "policy_version": "2026-10"}, "run_id": "refund-review-42"}]}'
HTTP 201

{
  "receipts": [
    {
      "event_id": "aev_scoped",
      "sequence": "3",
      "duplicate": false
    }
  ],
  "tree_size": "3"
}

The issuing key pays. This token cannot export, extend retention, or administer the ledger.

API reference

Try it

Sign in to try the API here with one of your team's keys. Requests use that key's permissions and spending limit. Standard charges apply.

Ledgers

Retention, sealing, and extensions

A ledger and all its events share one expiry. You prepay a 1 MiB allocation when you create a ledger (1 to 365 days, 30 by default), and an 8 KiB allocation for each new event until the ledger expires, rounded up to whole days. An event added just before expiry is retained only until that expiry. To keep collecting events over time, create new ledgers regularly, such as once a month. Every ledger read shows remaining_seconds and remaining_days.

  • POST /ledgers/{ledger_id}/extensions with days and max_cost_usd adds 1 to 365 days, at most 365 days from now. The extension charges for the ledger's 1 MiB and 8 KiB for every retained event, including erased events. A retained artifact can't expire before the new expiry. Extend again later for more.
  • POST /ledgers/{ledger_id}/seal stops appends for good. Reads, proofs, exports, and extensions continue until expiry.

Both require If-Match with the ledger's ETag, which only seals and extensions change, so appends don't invalidate it. Each signs a control receipt. A ledger allows 256 control receipts, with the last slot reserved for deletion or expiry. Sealing, extensions, and event erasures use the other slots. You can't shorten retention, reopen a sealed ledger, or rename it. Retention doesn't renew automatically. At expiry, event reads, proofs, appends, and new exports stop immediately; deletion runs in the background.

Errors and recovery

Errors have a code, a message, and retryable, and never contain event contents. Retry 429, 503, and 409 request_in_progress after Retry-After, with the same Idempotency-Key.

  • 409 event_conflict: the client event ID belongs to an event with different content or a different submitter. Retry the original event with the original credential, or use a new ID for a new event.
  • 409 ledger_sealed and 410 ledger_expired: create another ledger.
  • 412 stale_revision: read the ledger and retry with its new ETag.
  • 409 evidence_expiring or evidence_unavailable: extend or replace the artifact.
  • 402 max_cost_exceeded or token_budget_exceeded: raise the cap, or issue a new token.
  • 409 snapshot_invalidated: start the query or export again.
  • 404 checkpoint_not_found: use a tree size from a receipt or the latest checkpoint.
  • 409 capacity_exceeded: the error's limit names the limit.
  • 503 signing_unavailable: Motor can't sign; nothing was appended or charged.

Notifications

Subscribe with POST /meta/event_subscriptions and service: "audit_logs", at most two endpoints for each team. Events are audit_logs.ledger.expiring (72 hours ahead, or at creation for a shorter retention), .ledger.expired, .ledger.sealed, .export.succeeded, .export.failed, .export.canceled, and .erasure.completed. Their bodies have IDs, states, times, and costs, never event contents, actor IDs, or URLs. There's no event for each append: poll the feed.

POST/audit_logs/ledgers

Create a ledger

Creates a ledger and signs its empty checkpoint. Every event in the ledger shares its expiry, so a late event gets the time that remains. Costs one work unit and 1 MiB of retention for each day: $0.002048576 for 30 days. Credentials: a secret key.

ParameterLocationDescription
Idempotency-Keyheader1 to 128 printable ASCII characters: a new value for each operation, and the same value when you retry it. A retry with the same credential replays the first response for 35 days.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Request body
{
  "name": "Agent actions",
  "retention_days": 30,
  "metadata": {
    "team": "payments"
  },
  "max_cost_usd": "0.01"
}
FieldTypeDescription
namerequiredstring1 to 128 UTF-8 bytes. It never changes.
retention_daysinteger1 to 365, 30 by default. Every event in the ledger shares its expiry.
metadataobjectAt most 16 string pairs: keys of 1 to 64 UTF-8 bytes, values of at most 256, and at most 2 KiB in all. Metadata never changes.
max_cost_usdstringThe most that the creation may cost, such as "0.01".
Responses

201The ledger.

{
  "id": "ald_LrB528wYtd4M8VJwtZikAt",
  "object": "audit_ledger",
  "state": "open",
  "created_at": "2026-10-18T09:00:00.000000Z",
  "expires_at": "2026-11-17T09:00:00.000000Z",
  "remaining_seconds": "2592000",
  "remaining_days": "30",
  "revision": "1",
  "etag": "ald_LrB528wYtd4M8VJwtZikAt:1",
  "deletion": null,
  "name": "Agent actions",
  "metadata": {
    "team": "payments"
  },
  "retention_days": "30",
  "sealed_at": null,
  "tree_size": "0",
  "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "checkpoint": {
    "tree_size": "0",
    "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "issued_at": "2026-10-18T09:00:00.000000Z",
    "key_id": "development-1",
    "algorithm": "ecdsa-p256-sha256",
    "signed_body": "{\"issued_at\":\"2026-10-18T09:00:00.000000Z\",\"key_id\":\"development-1\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"root_hash\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\",\"tree_size\":\"0\",\"version\":\"motor-audit-checkpoint-v1\"}",
    "signature": "RDCKx2NJ7m1EHKF3RDihL62etRo_98qTbLcv5tOuNOzCc5GEKHA0mtcGUkA-zcq9ahIkND10RQg8Vkti0FveGA"
  },
  "events": "0",
  "billable_bytes": "1048576",
  "paid_through": "2026-11-17T09:00:00.000000Z",
  "erasure_generation": "0",
  "retained_artifacts": 0,
  "controls": {
    "used": 0,
    "remaining": 256
  },
  "bounds": {
    "max_event_bytes": 4096,
    "max_batch_events": 100,
    "max_targets": 8,
    "max_evidence": 8,
    "max_page_items": 100,
    "max_page_bytes": 131072,
    "max_candidates_per_page": "1000",
    "max_extension_horizon_days": "365",
    "max_controls": 256,
    "max_retained_artifacts": 100
  },
  "cost": {
    "rate_version": "2026-10-18",
    "charged_usd": "0.002048576"
  },
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "31457280",
    "charged_usd": "0.002048576"
  }
}

400The body isn't a JSON object, or an Idempotency-Key is missing.

{
  "error": {
    "code": "invalid_json",
    "message": "The request body isn't valid JSON. Expected a JSON value at byte 0.",
    "retryable": false
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

409A conflict. Codes: ledger_sealed, event_conflict, evidence_expiring, evidence_unavailable, snapshot_invalidated, capacity_exceeded, idempotency_conflict, request_in_progress.

{
  "error": {
    "code": "ledger_sealed",
    "message": "The ledger is sealed, so it accepts no more events. Create another ledger for new events.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

413The body is too large: 512 KiB for an append, and 64 KiB otherwise.

{
  "error": {
    "code": "body_too_large",
    "message": "The request body must be at most 524288 bytes.",
    "retryable": false
  }
}

415The body isn't application/json.

{
  "error": {
    "code": "unsupported_media_type",
    "message": "Send the request body as application/json.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

503Motor can't sign checkpoints (signing_unavailable), a ledger is under an integrity check (integrity_check), or the billing month is closing. Retry later.

{
  "error": {
    "code": "signing_unavailable",
    "message": "Motor can't sign checkpoints right now, so it doesn't accept new events. Motor didn't append or charge anything. Retry later.",
    "retryable": true,
    "retry_after_seconds": 60
  }
}

GET/audit_logs/ledgers

List ledgers

Returns the team's ledgers in creation order. Costs one work unit. Credentials: a secret key.

ParameterLocationDescription
statequery
limitquery1 to 100 items, 50 by default.
cursorquerynext_cursor of the previous page. It works for 15 minutes, with the same filters and credential.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200A page of ledgers.

{
  "data": [
    {
      "id": "ald_LrB528wYtd4M8VJwtZikAt",
      "object": "audit_ledger",
      "state": "open",
      "created_at": "2026-10-18T09:00:00.000000Z",
      "expires_at": "2026-11-17T09:00:00.000000Z",
      "remaining_seconds": "2592000",
      "remaining_days": "30",
      "revision": "1",
      "etag": "ald_LrB528wYtd4M8VJwtZikAt:1",
      "deletion": null,
      "name": "Agent actions",
      "metadata": {
        "team": "payments"
      },
      "retention_days": "30",
      "sealed_at": null,
      "tree_size": "0",
      "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "checkpoint": {
        "tree_size": "0",
        "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
        "issued_at": "2026-10-18T09:00:00.000000Z",
        "key_id": "development-1",
        "algorithm": "ecdsa-p256-sha256",
        "signed_body": "{\"issued_at\":\"2026-10-18T09:00:00.000000Z\",\"key_id\":\"development-1\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"root_hash\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\",\"tree_size\":\"0\",\"version\":\"motor-audit-checkpoint-v1\"}",
        "signature": "RDCKx2NJ7m1EHKF3RDihL62etRo_98qTbLcv5tOuNOzCc5GEKHA0mtcGUkA-zcq9ahIkND10RQg8Vkti0FveGA"
      },
      "events": "0",
      "billable_bytes": "1048576",
      "paid_through": "2026-11-17T09:00:00.000000Z",
      "erasure_generation": "0",
      "retained_artifacts": 0,
      "controls": {
        "used": 0,
        "remaining": 256
      },
      "bounds": {
        "max_event_bytes": 4096,
        "max_batch_events": 100,
        "max_targets": 8,
        "max_evidence": 8,
        "max_page_items": 100,
        "max_page_bytes": 131072,
        "max_candidates_per_page": "1000",
        "max_extension_horizon_days": "365",
        "max_controls": 256,
        "max_retained_artifacts": 100
      },
      "cost": {
        "rate_version": "2026-10-18",
        "charged_usd": "0.002048576"
      }
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

GET/audit_logs/ledgers/{ledger_id}

Get a ledger

Returns the ledger, its configuration ETag, its tree size and root, its paid expiry, its bounds, and its billable bytes. A ledger that's being deleted shows its deletion. Costs one work unit. Credentials: a secret key or an audit_logs:read token for the ledger without a run restriction.

ParameterLocationDescription
ledger_idpath
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200The ledger.

{
  "id": "ald_LrB528wYtd4M8VJwtZikAt",
  "object": "audit_ledger",
  "state": "open",
  "created_at": "2026-10-18T09:00:00.000000Z",
  "expires_at": "2026-11-17T09:00:00.000000Z",
  "remaining_seconds": "2592000",
  "remaining_days": "30",
  "revision": "1",
  "etag": "ald_LrB528wYtd4M8VJwtZikAt:1",
  "deletion": null,
  "name": "Agent actions",
  "metadata": {
    "team": "payments"
  },
  "retention_days": "30",
  "sealed_at": null,
  "tree_size": "0",
  "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "checkpoint": {
    "tree_size": "0",
    "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "issued_at": "2026-10-18T09:00:00.000000Z",
    "key_id": "development-1",
    "algorithm": "ecdsa-p256-sha256",
    "signed_body": "{\"issued_at\":\"2026-10-18T09:00:00.000000Z\",\"key_id\":\"development-1\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"root_hash\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\",\"tree_size\":\"0\",\"version\":\"motor-audit-checkpoint-v1\"}",
    "signature": "RDCKx2NJ7m1EHKF3RDihL62etRo_98qTbLcv5tOuNOzCc5GEKHA0mtcGUkA-zcq9ahIkND10RQg8Vkti0FveGA"
  },
  "events": "0",
  "billable_bytes": "1048576",
  "paid_through": "2026-11-17T09:00:00.000000Z",
  "erasure_generation": "0",
  "retained_artifacts": 0,
  "controls": {
    "used": 0,
    "remaining": 256
  },
  "bounds": {
    "max_event_bytes": 4096,
    "max_batch_events": 100,
    "max_targets": 8,
    "max_evidence": 8,
    "max_page_items": 100,
    "max_page_bytes": 131072,
    "max_candidates_per_page": "1000",
    "max_extension_horizon_days": "365",
    "max_controls": 256,
    "max_retained_artifacts": 100
  },
  "cost": {
    "rate_version": "2026-10-18",
    "charged_usd": "0.002048576"
  },
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

DELETE/audit_logs/ledgers/{ledger_id}

Delete a ledger

Deletes the ledger and everything in it. Every read stops at once; the returned operation tracks background cleanup; poll it until it succeeds. Customers' retained artifacts stay in Artifact storage. Prepaid retention isn't refunded. Costs one work unit when the key can pay; otherwise the deletion is included. Credentials: a secret key. Erasure is irreversible and removes data from the live service. Encrypted historical database versions currently don't have a fixed deletion deadline. Download URLs already issued may remain usable until they expire; downloaded copies can't be recalled.

ParameterLocationDescription
ledger_idpath
If-MatchheaderThe ledger's ETag in quotes, such as "ald_...:2". It names the configuration revision, which appends don't change.
Idempotency-Keyheader1 to 128 printable ASCII characters: a new value for each operation, and the same value when you retry it. A retry with the same credential replays the first response for 35 days.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

202The deletion's operation.

{
  "operation": {
    "id": "aop_HpWa3MCTWRzisojbcqWptL",
    "object": "audit_operation",
    "kind": "deletion",
    "state": "pending",
    "reason_code": "customer_request",
    "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
    "event_ids": [],
    "phase": "exports",
    "counts": {
      "events": "0",
      "erased": "0",
      "removed_rows": "0"
    },
    "retry": {
      "attempts": 0,
      "error_code": null
    },
    "created_at": "2026-10-18T09:00:45.000000Z",
    "updated_at": "2026-10-18T09:00:45.000000Z",
    "finished_at": null,
    "deadline_at": "2026-10-19T09:00:45.000000Z"
  },
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

400The body isn't a JSON object, or an Idempotency-Key is missing.

{
  "error": {
    "code": "invalid_json",
    "message": "The request body isn't valid JSON. Expected a JSON value at byte 0.",
    "retryable": false
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

409A conflict. Codes: ledger_sealed, event_conflict, evidence_expiring, evidence_unavailable, snapshot_invalidated, capacity_exceeded, idempotency_conflict, request_in_progress.

{
  "error": {
    "code": "ledger_sealed",
    "message": "The ledger is sealed, so it accepts no more events. Create another ledger for new events.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

412The If-Match revision is stale: stale_revision.

{
  "error": {
    "code": "stale_revision",
    "message": "The ledger's configuration changed since you read it. Read it again and retry with its new ETag.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

428The request needs If-Match: precondition_required.

{
  "error": {
    "code": "precondition_required",
    "message": "Send the ETag of the resource in the If-Match header, such as If-Match: \"ff:ffe_...:7\". Read the resource to get its current ETag.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

503Motor can't sign checkpoints (signing_unavailable), a ledger is under an integrity check (integrity_check), or the billing month is closing. Retry later.

{
  "error": {
    "code": "signing_unavailable",
    "message": "Motor can't sign checkpoints right now, so it doesn't accept new events. Motor didn't append or charge anything. Retry later.",
    "retryable": true,
    "retry_after_seconds": 60
  }
}

POST/audit_logs/ledgers/{ledger_id}/seal

Seal a ledger

Stops appends for good and signs a control receipt. Reads, proofs, exports, and extensions continue until expiry. A sealed ledger answers with its state. Costs one work unit. Credentials: a secret key.

ParameterLocationDescription
ledger_idpath
If-MatchheaderThe ledger's ETag in quotes, such as "ald_...:2". It names the configuration revision, which appends don't change.
Idempotency-Keyheader1 to 128 printable ASCII characters: a new value for each operation, and the same value when you retry it. A retry with the same credential replays the first response for 35 days.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Request body
{}
Responses

200The sealed ledger.

{
  "id": "ald_LrB528wYtd4M8VJwtZikAt",
  "object": "audit_ledger",
  "state": "sealed",
  "created_at": "2026-10-18T09:00:00.000000Z",
  "expires_at": "2026-11-17T09:00:00.000000Z",
  "remaining_seconds": "2592000",
  "remaining_days": "30",
  "revision": "2",
  "etag": "ald_LrB528wYtd4M8VJwtZikAt:2",
  "deletion": null,
  "name": "Agent actions",
  "metadata": {
    "team": "payments"
  },
  "retention_days": "30",
  "sealed_at": "2026-10-18T09:00:43.000000Z",
  "tree_size": "0",
  "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "checkpoint": {
    "tree_size": "0",
    "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "issued_at": "2026-10-18T09:00:00.000000Z",
    "key_id": "development-1",
    "algorithm": "ecdsa-p256-sha256",
    "signed_body": "{\"issued_at\":\"2026-10-18T09:00:00.000000Z\",\"key_id\":\"development-1\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"root_hash\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\",\"tree_size\":\"0\",\"version\":\"motor-audit-checkpoint-v1\"}",
    "signature": "RDCKx2NJ7m1EHKF3RDihL62etRo_98qTbLcv5tOuNOzCc5GEKHA0mtcGUkA-zcq9ahIkND10RQg8Vkti0FveGA"
  },
  "events": "0",
  "billable_bytes": "1048576",
  "paid_through": "2026-11-17T09:00:00.000000Z",
  "erasure_generation": "0",
  "retained_artifacts": 0,
  "controls": {
    "used": 1,
    "remaining": 255
  },
  "bounds": {
    "max_event_bytes": 4096,
    "max_batch_events": 100,
    "max_targets": 8,
    "max_evidence": 8,
    "max_page_items": 100,
    "max_page_bytes": 131072,
    "max_candidates_per_page": "1000",
    "max_extension_horizon_days": "365",
    "max_controls": 256,
    "max_retained_artifacts": 100
  },
  "cost": {
    "rate_version": "2026-10-18",
    "charged_usd": "0.002048576"
  },
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  },
  "control_receipt": {
    "kind": "seal",
    "index": 0,
    "issued_at": "2026-10-18T09:00:10.000000Z",
    "key_id": "development-1",
    "algorithm": "ecdsa-p256-sha256",
    "signed_body": "{\"expires_at_after\":\"2026-11-17T09:00:00.000000Z\",\"expires_at_before\":\"2026-11-17T09:00:00.000000Z\",\"index\":\"0\",\"issued_at\":\"2026-10-18T09:00:10.000000Z\",\"key_id\":\"development-1\",\"kind\":\"seal\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"revision_after\":\"2\",\"revision_before\":\"1\",\"tree_size\":\"4\",\"version\":\"motor-audit-control-v1\"}",
    "signature": "KPPDcGesEqF-QcPLWlA4sNPi_0ZIf81Rv-t2rQpcpu_hrx88t0AoYKlJcG_pjnOURMwM6l3yyPb7i9ULKZz9xA"
  }
}

400The body isn't a JSON object, or an Idempotency-Key is missing.

{
  "error": {
    "code": "invalid_json",
    "message": "The request body isn't valid JSON. Expected a JSON value at byte 0.",
    "retryable": false
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

409A conflict. Codes: ledger_sealed, event_conflict, evidence_expiring, evidence_unavailable, snapshot_invalidated, capacity_exceeded, idempotency_conflict, request_in_progress.

{
  "error": {
    "code": "ledger_sealed",
    "message": "The ledger is sealed, so it accepts no more events. Create another ledger for new events.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

410The ledger's paid retention ended: ledger_expired. Read the ledger for its deletion status.

{
  "error": {
    "code": "ledger_expired",
    "message": "The ledger's paid retention ended at 2026-11-17T09:00:00.000Z, so its events can't be read, proven, exported, or appended. Read the ledger for its deletion status.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

412The If-Match revision is stale: stale_revision.

{
  "error": {
    "code": "stale_revision",
    "message": "The ledger's configuration changed since you read it. Read it again and retry with its new ETag.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

415The body isn't application/json.

{
  "error": {
    "code": "unsupported_media_type",
    "message": "Send the request body as application/json.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

428The request needs If-Match: precondition_required.

{
  "error": {
    "code": "precondition_required",
    "message": "Send the ETag of the resource in the If-Match header, such as If-Match: \"ff:ffe_...:7\". Read the resource to get its current ETag.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

503Motor can't sign checkpoints (signing_unavailable), a ledger is under an integrity check (integrity_check), or the billing month is closing. Retry later.

{
  "error": {
    "code": "signing_unavailable",
    "message": "Motor can't sign checkpoints right now, so it doesn't accept new events. Motor didn't append or charge anything. Retry later.",
    "retryable": true,
    "retry_after_seconds": 60
  }
}

POST/audit_logs/ledgers/{ledger_id}/extensions

Extend a ledger's retention

Adds days to the ledger's expiry, at most 365 days from now, and signs a control receipt. It pays 1 MiB and 8 KiB for every retained event, tombstones included, for each added day. A retained artifact with an expires_at before the new expiry makes the extension refuse with evidence_expiring. Costs one work unit and the retention. Credentials: a secret key.

ParameterLocationDescription
ledger_idpath
If-MatchheaderThe ledger's ETag in quotes, such as "ald_...:2". It names the configuration revision, which appends don't change.
Idempotency-Keyheader1 to 128 printable ASCII characters: a new value for each operation, and the same value when you retry it. A retry with the same credential replays the first response for 35 days.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Request body
{
  "days": 10,
  "max_cost_usd": "0.1"
}
FieldTypeDescription
daysrequiredintegerDays to add: 1 to 365, ending at most 365 days from now.
max_cost_usdrequiredstringRequired: the most that the extension may cost.
Responses

200The extended ledger.

{
  "id": "ald_LrB528wYtd4M8VJwtZikAt",
  "object": "audit_ledger",
  "state": "open",
  "created_at": "2026-10-18T09:00:00.000000Z",
  "expires_at": "2026-11-27T09:00:00.000000Z",
  "remaining_seconds": "3455990",
  "remaining_days": "40",
  "revision": "2",
  "etag": "ald_LrB528wYtd4M8VJwtZikAt:2",
  "deletion": null,
  "name": "Agent actions",
  "metadata": {
    "team": "payments"
  },
  "retention_days": "30",
  "sealed_at": null,
  "tree_size": "0",
  "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "checkpoint": {
    "tree_size": "0",
    "root_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "issued_at": "2026-10-18T09:00:00.000000Z",
    "key_id": "development-1",
    "algorithm": "ecdsa-p256-sha256",
    "signed_body": "{\"issued_at\":\"2026-10-18T09:00:00.000000Z\",\"key_id\":\"development-1\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"root_hash\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\",\"tree_size\":\"0\",\"version\":\"motor-audit-checkpoint-v1\"}",
    "signature": "RDCKx2NJ7m1EHKF3RDihL62etRo_98qTbLcv5tOuNOzCc5GEKHA0mtcGUkA-zcq9ahIkND10RQg8Vkti0FveGA"
  },
  "events": "0",
  "billable_bytes": "1048576",
  "paid_through": "2026-11-27T09:00:00.000000Z",
  "erasure_generation": "0",
  "retained_artifacts": 0,
  "controls": {
    "used": 1,
    "remaining": 255
  },
  "bounds": {
    "max_event_bytes": 4096,
    "max_batch_events": 100,
    "max_targets": 8,
    "max_evidence": 8,
    "max_page_items": 100,
    "max_page_bytes": 131072,
    "max_candidates_per_page": "1000",
    "max_extension_horizon_days": "365",
    "max_controls": 256,
    "max_retained_artifacts": 100
  },
  "cost": {
    "rate_version": "2026-10-18",
    "charged_usd": "0.002048576"
  },
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "10731520",
    "charged_usd": "0.001357717333"
  },
  "control_receipt": {
    "kind": "extension",
    "index": 0,
    "issued_at": "2026-10-18T09:00:10.000000Z",
    "key_id": "development-1",
    "algorithm": "ecdsa-p256-sha256",
    "signed_body": "{\"expires_at_after\":\"2026-11-27T09:00:00.000000Z\",\"expires_at_before\":\"2026-11-17T09:00:00.000000Z\",\"index\":\"0\",\"issued_at\":\"2026-10-18T09:00:10.000000Z\",\"key_id\":\"development-1\",\"kind\":\"extension\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"revision_after\":\"2\",\"revision_before\":\"1\",\"tree_size\":\"4\",\"version\":\"motor-audit-control-v1\"}",
    "signature": "KPPDcGesEqF-QcPLWlA4sNPi_0ZIf81Rv-t2rQpcpu_hrx88t0AoYKlJcG_pjnOURMwM6l3yyPb7i9ULKZz9xA"
  }
}

400The body isn't a JSON object, or an Idempotency-Key is missing.

{
  "error": {
    "code": "invalid_json",
    "message": "The request body isn't valid JSON. Expected a JSON value at byte 0.",
    "retryable": false
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

409A conflict. Codes: ledger_sealed, event_conflict, evidence_expiring, evidence_unavailable, snapshot_invalidated, capacity_exceeded, idempotency_conflict, request_in_progress.

{
  "error": {
    "code": "ledger_sealed",
    "message": "The ledger is sealed, so it accepts no more events. Create another ledger for new events.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

410The ledger's paid retention ended: ledger_expired. Read the ledger for its deletion status.

{
  "error": {
    "code": "ledger_expired",
    "message": "The ledger's paid retention ended at 2026-11-17T09:00:00.000Z, so its events can't be read, proven, exported, or appended. Read the ledger for its deletion status.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

412The If-Match revision is stale: stale_revision.

{
  "error": {
    "code": "stale_revision",
    "message": "The ledger's configuration changed since you read it. Read it again and retry with its new ETag.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

415The body isn't application/json.

{
  "error": {
    "code": "unsupported_media_type",
    "message": "Send the request body as application/json.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

428The request needs If-Match: precondition_required.

{
  "error": {
    "code": "precondition_required",
    "message": "Send the ETag of the resource in the If-Match header, such as If-Match: \"ff:ffe_...:7\". Read the resource to get its current ETag.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

503Motor can't sign checkpoints (signing_unavailable), a ledger is under an integrity check (integrity_check), or the billing month is closing. Retry later.

{
  "error": {
    "code": "signing_unavailable",
    "message": "Motor can't sign checkpoints right now, so it doesn't accept new events. Motor didn't append or charge anything. Retry later.",
    "retryable": true,
    "retry_after_seconds": 60
  }
}

Events

Events and their fields

An event is strict JSON of at most 4,096 bytes after normalization. Motor refuses unknown fields, duplicate member names, nulls, invalid UTF-8, nesting deeper than 8, and numbers that aren't integers from -9007199254740991 to 9007199254740991. Send other numbers, such as amounts, as strings. Leave out an optional field instead of sending null.

  • client_event_id (required): 1 to 128 printable ASCII characters. It's unique in the ledger for the ledger's whole life.
  • action (required): 1 to 128 of A-Z a-z 0-9 _ . : -, such as payment.submit.
  • actor (required): {"type", "id"} with a type of agent, user, service, or system, and an optional on_behalf_of of the same form, one level deep.
  • targets (required): 1 to 8 {"type", "id"} pairs.
  • outcome (required): attempted, succeeded, failed, denied, or unknown. A later outcome is another event with parent_event_id, not an edit.
  • occurred_at (required): RFC 3339, from 1970 to 5 minutes after Motor receives it. Motor stores it in UTC to the microsecond. Late events are fine; they never reorder the sequence.
  • run_id, trace_id, span_id: opaque strings of 1 to 128 bytes.
  • parent_event_id (an event of this ledger) or parent_client_event_id (an earlier event of the same request), not both.
  • authority: {"kind", "reference", "policy_version", "scopes"}. It's a claim, never a bearer token or a verified approval.
  • cost: {"amount", "currency", "kind"}, an amount such as "12.50" with at most 12 decimals, a 3-letter currency, and estimated or actual. Motor records it; it never bills or converts it.
  • evidence: at most 8 references. See Evidence.
  • attributes: any JSON object within the event's size. Motor doesn't index it, and refuses credential-like member names (authorization, cookie, password, secret, access_token, refresh_token, api_key) at any depth. This doesn't catch every secret: don't record secrets or unnecessary personal data.

Motor wraps each event in an envelope that you can't set: its ID, the ledger, the time Motor received it, a random salt, the source, and the submitter (the key, the client token, and the key's principal, by ID). Everything that you send is an assertion. To correct an event, append action: "record.corrected" with the earlier event as its target and the corrected facts in attributes.

Batches, duplicates, and lost responses

Batch your events: a request costs one work unit whether it carries 1 event or 100. If any event is invalid, refers to unavailable evidence, or would exceed a limit, nothing commits and nothing is charged.

If you lose a response, retry the same request with the same Idempotency-Key within 35 days: you get the original receipt, and nothing is appended or charged again. After that, the ledger still knows every client_event_id. An identical event from the same submitter gets its first receipt with duplicate: true, an event with a used ID and different content or a different submitter gets 409 event_conflict, and an erased event gets its tombstone. A new request containing only duplicates costs one work unit and appends nothing.

max_cost_usd caps the batch's cost. By default, the cap covers one work unit plus append and retention charges as if every event in the batch were new. The receipt's usage shows the work units, the new events, the retained byte-days, and what Motor charged.

Queries and the feed

GET /ledgers/{ledger_id}/events reads one ledger in ascending sequence. Filters use exact matches, and an event must match every filter: actor_type and actor_id, target_type and target_id, action, run_id, trace_id, outcome, and source_kind, and the ranges received_from/received_before and occurred_from/occurred_before (from inclusive, before exclusive). There's no wildcard, OR, or full-text search.

Each page examines at most 1,000 candidate events and returns at most 100 events or 128 KiB. A page can be empty and still have a next_cursor: keep following it until has_more is false. The first page fixes snapshot_tree_size, so later pages never show later appends; finish following its cursors, then start a new query with after_sequence set to the final last_examined_sequence. A cursor works for 15 minutes from the first page, with the same filters and credential. If someone erases an event of the ledger meanwhile, the next page answers 409 snapshot_invalidated: start again. Query results don’t include cryptographic proof that every matching event was returned.

For a feed, poll with after_sequence and back off from 1 to 30 seconds while pages are empty. Each ledger takes at most 2 feed requests a second, and each page, empty or not, is one work unit. Motor doesn't send an event for each append.

import time, requests
# Set ledger and key, and define handle(event) before running.
after, cursor, wait = 0, None, 1
while True:
    params = {"cursor": cursor} if cursor else {"after_sequence": after}
    response = requests.get(
        f"https://api.motor.dev/audit_logs/ledgers/{ledger}/events",
        params=params, headers={"Authorization": f"Bearer {key}"}, timeout=30)
    if response.status_code in (429, 503):
        time.sleep(max(1, int(response.headers.get("Retry-After", "5"))))
        continue
    response.raise_for_status()
    page = response.json()
    for event in page["data"]:
        handle(event)
    cursor = page["next_cursor"]
    if cursor:
        time.sleep(0.5)
        continue
    after = int(page["last_examined_sequence"])
    wait = 1 if page["data"] else min(wait * 2, 30)
    time.sleep(wait)

GET /ledgers/{ledger_id}/events/{event_id} returns one event with its leaf hash; include_canonical=true adds its exact canonical bytes in Base64.

POST/audit_logs/ledgers/{ledger_id}/events

Append events

Appends 1 to 100 events atomically: either all of them commit, or Motor doesn't commit any. Sequences follow the request's order, and Motor signs the new checkpoint before it answers. A repeated client_event_id with the same content and submitter returns its first receipt without another append or retention charge; a new request still costs one work unit. Different content or a different submitter returns event_conflict. Recover a lost response by retrying with the same Idempotency-Key. Costs one work unit, and for each new event $0.000005 and 8 KiB of retention until the ledger expires. Credentials: a secret key, or an append token of the ledger.

ParameterLocationDescription
ledger_idpath
Idempotency-Keyheader1 to 128 printable ASCII characters: a new value for each operation, and the same value when you retry it. A retry with the same credential replays the first response for 35 days.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Request body
{
  "events": [
    {
      "client_event_id": "e1",
      "action": "payment.submit",
      "actor": {
        "type": "agent",
        "id": "agent_7"
      },
      "targets": [
        {
          "type": "invoice",
          "id": "inv_9"
        }
      ],
      "outcome": "attempted",
      "occurred_at": "2026-10-18T08:59:00Z",
      "run_id": "run_1",
      "authority": {
        "kind": "approval",
        "reference": "apr_7",
        "scopes": [
          "payments:submit"
        ]
      },
      "cost": {
        "amount": "0.25",
        "currency": "USD",
        "kind": "estimated"
      },
      "evidence": [
        {
          "artifact_id": "art_Co8jNxhZn3uCz6ZmBLYx6w",
          "mode": "retain"
        },
        {
          "uri": "https://example.com/receipt",
          "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        }
      ],
      "attributes": {
        "attempt": 1
      }
    },
    {
      "client_event_id": "e2",
      "action": "payment.submit",
      "actor": {
        "type": "agent",
        "id": "agent_7"
      },
      "targets": [
        {
          "type": "invoice",
          "id": "inv_9"
        }
      ],
      "outcome": "succeeded",
      "occurred_at": "2026-10-18T08:59:30Z",
      "run_id": "run_1",
      "parent_client_event_id": "e1"
    }
  ],
  "max_cost_usd": "0.01"
}
FieldTypeDescription
eventsrequiredarray
max_cost_usdstringThe most that the append may cost. The default is the cost of the request if every event is new.
Responses

201The receipts and the signed checkpoint.

{
  "object": "audit_append_receipt",
  "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
  "receipts": [
    {
      "event_id": "aev_4hya3wkMnJMhbAKFeiScRN",
      "sequence": "1",
      "leaf_hash": "c2660305f1b4886aac394ef391f29ab3f4ba66a659a7a5c35168f66a69ae9939",
      "duplicate": false,
      "payload_erased": false
    },
    {
      "event_id": "aev_6edPveaagHj5aBDcfdUsps",
      "sequence": "2",
      "leaf_hash": "cb58d17147bd3276f0bf987fbb173e1015f2f8c7e584ef627d7f359e6927b4e6",
      "duplicate": false,
      "payload_erased": false
    }
  ],
  "tree_size": "2",
  "checkpoint": {
    "tree_size": "2",
    "root_hash": "abf3716c7edabb20fa0bd526cae3e4edd20a18964bcbc0fc8d14ce1414b00dab",
    "issued_at": "2026-10-18T09:00:02.000000Z",
    "key_id": "development-1",
    "algorithm": "ecdsa-p256-sha256",
    "signed_body": "{\"issued_at\":\"2026-10-18T09:00:02.000000Z\",\"key_id\":\"development-1\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"root_hash\":\"abf3716c7edabb20fa0bd526cae3e4edd20a18964bcbc0fc8d14ce1414b00dab\",\"tree_size\":\"2\",\"version\":\"motor-audit-checkpoint-v1\"}",
    "signature": "RDCKx2NJ7m1EHKF3RDihL62etRo_98qTbLcv5tOuNOzCc5GEKHA0mtcGUkA-zcq9ahIkND10RQg8Vkti0FveGA"
  },
  "paid_through": "2026-11-17T09:00:00.000000Z",
  "remaining_seconds": "2591998",
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "2",
    "retained_byte_days": "491520",
    "charged_usd": "0.001026384"
  }
}

400The body isn't a JSON object, or an Idempotency-Key is missing.

{
  "error": {
    "code": "invalid_json",
    "message": "The request body isn't valid JSON. Expected a JSON value at byte 0.",
    "retryable": false
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

409A conflict. Codes: ledger_sealed, event_conflict, evidence_expiring, evidence_unavailable, snapshot_invalidated, capacity_exceeded, idempotency_conflict, request_in_progress.

{
  "error": {
    "code": "ledger_sealed",
    "message": "The ledger is sealed, so it accepts no more events. Create another ledger for new events.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

413The body is too large: 512 KiB for an append, and 64 KiB otherwise.

{
  "error": {
    "code": "body_too_large",
    "message": "The request body must be at most 524288 bytes.",
    "retryable": false
  }
}

415The body isn't application/json.

{
  "error": {
    "code": "unsupported_media_type",
    "message": "Send the request body as application/json.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

503Motor can't sign checkpoints (signing_unavailable), a ledger is under an integrity check (integrity_check), or the billing month is closing. Retry later.

{
  "error": {
    "code": "signing_unavailable",
    "message": "Motor can't sign checkpoints right now, so it doesn't accept new events. Motor didn't append or charge anything. Retry later.",
    "retryable": true,
    "retry_after_seconds": 60
  }
}

410The ledger's paid retention ended: ledger_expired. Read the ledger for its deletion status.

{
  "error": {
    "code": "ledger_expired",
    "message": "The ledger's paid retention ended at 2026-11-17T09:00:00.000Z, so its events can't be read, proven, exported, or appended. Read the ledger for its deletion status.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

GET/audit_logs/ledgers/{ledger_id}/events

List events

Returns a bounded page of the ledger's events in ascending sequence. Filters use exact matches, and an event must match every filter. Each page examines at most 1,000 candidates, so an empty page can still have a next cursor. The first page captures the tree size. An erasure in the ledger invalidates older cursors with snapshot_invalidated. The feed takes at most 2 requests a second for each ledger. Costs one work unit, even for an empty page. Credentials: a secret key, or a read token of the ledger.

ParameterLocationDescription
ledger_idpath
actor_typequeryWith actor_id: agent, user, service, or system.
actor_idqueryWith actor_type.
target_typequeryWith target_id.
target_idqueryWith target_type.
actionqueryAn exact action.
run_idqueryAn exact run.
trace_idqueryAn exact trace.
outcomequeryattempted, succeeded, failed, denied, or unknown.
source_kindquerycustomer or motor.
received_fromqueryAn inclusive lower bound of received_at.
received_beforequeryAn exclusive upper bound of received_at.
occurred_fromqueryAn inclusive lower bound of occurred_at.
occurred_beforequeryAn exclusive upper bound of occurred_at.
after_sequencequeryStart after this sequence.
through_sequencequeryStop at this sequence.
include_canonicalquerytrue adds canonical_base64, which counts against the page's 128 KiB.
limitquery1 to 100 items, 50 by default.
cursorquerynext_cursor of the previous page. It works for 15 minutes, with the same filters and credential.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200A page of events.

{
  "data": [
    {
      "id": "aev_4hya3wkMnJMhbAKFeiScRN",
      "object": "audit_event",
      "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
      "sequence": "1",
      "leaf_hash": "c2660305f1b4886aac394ef391f29ab3f4ba66a659a7a5c35168f66a69ae9939",
      "received_at": "2026-10-18T09:00:02.000000Z",
      "source_kind": "customer",
      "payload_erased": false,
      "source": {
        "kind": "customer"
      },
      "submitter": {
        "credential": "secret_key",
        "key_id": "key_NDxHjEB5ddPCBkhcm2SLP6",
        "principal_id": "user_WSJsCZHdzwgnV6LMCtKUqD"
      },
      "parent_event_id": null,
      "evidence": [
        {
          "artifact_id": "art_Co8jNxhZn3uCz6ZmBLYx6w",
          "digest_status": "verified",
          "mode": "retain",
          "sha256": "9e85aa95f04db5f108534e48b63e75e8045a7ecab59988405e70a9260300a0d6",
          "size_bytes": "13"
        }
      ],
      "event": {
        "action": "payment.submit",
        "actor": {
          "id": "agent_7",
          "type": "agent"
        },
        "attributes": {
          "attempt": 1
        },
        "authority": {
          "kind": "approval",
          "reference": "apr_7",
          "scopes": [
            "payments:submit"
          ]
        },
        "client_event_id": "e1",
        "cost": {
          "amount": "0.25",
          "currency": "USD",
          "kind": "estimated"
        },
        "evidence": [
          {
            "artifact_id": "art_Co8jNxhZn3uCz6ZmBLYx6w",
            "mode": "retain"
          },
          {
            "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            "uri": "https://example.com/receipt"
          }
        ],
        "occurred_at": "2026-10-18T08:59:00.000000Z",
        "outcome": "attempted",
        "run_id": "run_1",
        "targets": [
          {
            "id": "inv_9",
            "type": "invoice"
          }
        ]
      }
    }
  ],
  "has_more": true,
  "next_cursor": "eyJ2IjozLCJzIjoi...",
  "snapshot_tree_size": "4",
  "last_examined_sequence": "1",
  "scanned": "1",
  "access_path": "actor",
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

409A conflict. Codes: ledger_sealed, event_conflict, evidence_expiring, evidence_unavailable, snapshot_invalidated, capacity_exceeded, idempotency_conflict, request_in_progress.

{
  "error": {
    "code": "ledger_sealed",
    "message": "The ledger is sealed, so it accepts no more events. Create another ledger for new events.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

410The ledger's paid retention ended: ledger_expired. Read the ledger for its deletion status.

{
  "error": {
    "code": "ledger_expired",
    "message": "The ledger's paid retention ended at 2026-11-17T09:00:00.000Z, so its events can't be read, proven, exported, or appended. Read the ledger for its deletion status.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

GET/audit_logs/ledgers/{ledger_id}/events/{event_id}

Get an event

Returns the event and its leaf hash, or its tombstone after an erasure. An erased field never appears. Costs one work unit. Credentials: a secret key, or a read token of the ledger.

ParameterLocationDescription
ledger_idpath
event_idpath
include_canonicalquerytrue adds canonical_base64, which counts against the page's 128 KiB.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200The event or its tombstone.

{
  "id": "aev_4hya3wkMnJMhbAKFeiScRN",
  "object": "audit_event",
  "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
  "sequence": "1",
  "leaf_hash": "c2660305f1b4886aac394ef391f29ab3f4ba66a659a7a5c35168f66a69ae9939",
  "received_at": "2026-10-18T09:00:02.000000Z",
  "source_kind": "customer",
  "payload_erased": false,
  "source": {
    "kind": "customer"
  },
  "submitter": {
    "credential": "secret_key",
    "key_id": "key_NDxHjEB5ddPCBkhcm2SLP6",
    "principal_id": "user_WSJsCZHdzwgnV6LMCtKUqD"
  },
  "parent_event_id": null,
  "evidence": [
    {
      "artifact_id": "art_Co8jNxhZn3uCz6ZmBLYx6w",
      "digest_status": "verified",
      "mode": "retain",
      "sha256": "9e85aa95f04db5f108534e48b63e75e8045a7ecab59988405e70a9260300a0d6",
      "size_bytes": "13"
    }
  ],
  "event": {
    "action": "payment.submit",
    "actor": {
      "id": "agent_7",
      "type": "agent"
    },
    "attributes": {
      "attempt": 1
    },
    "authority": {
      "kind": "approval",
      "reference": "apr_7",
      "scopes": [
        "payments:submit"
      ]
    },
    "client_event_id": "e1",
    "cost": {
      "amount": "0.25",
      "currency": "USD",
      "kind": "estimated"
    },
    "evidence": [
      {
        "artifact_id": "art_Co8jNxhZn3uCz6ZmBLYx6w",
        "mode": "retain"
      },
      {
        "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "uri": "https://example.com/receipt"
      }
    ],
    "occurred_at": "2026-10-18T08:59:00.000000Z",
    "outcome": "attempted",
    "run_id": "run_1",
    "targets": [
      {
        "id": "inv_9",
        "type": "invoice"
      }
    ]
  },
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

410The ledger's paid retention ended: ledger_expired. Read the ledger for its deletion status.

{
  "error": {
    "code": "ledger_expired",
    "message": "The ledger's paid retention ended at 2026-11-17T09:00:00.000Z, so its events can't be read, proven, exported, or appended. Read the ledger for its deletion status.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

Client tokens

Give an agent a client token instead of your key. Your secret key issues it with POST /meta/client_tokens; Motor shows the token once.

curl https://api.motor.dev/meta/client_tokens \
  -H "Authorization: Bearer $MOTOR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"scopes": ["audit_logs:append"], "expires_in": 3600, "audit_logs": {
    "ledger_id": "ald_...", "actor": {"type": "agent", "id": "payments-agent"},
    "run_id": "run_42", "max_spend_usd": "0.50", "max_events": 1000}}'
  • An audit_logs:append token appends only to its ledger, only events of its actor and run, within max_spend_usd and max_events.
  • An audit_logs:read token queries and reads events within max_spend_usd. With a run_id, it reads only that run's events, and it can't read checkpoints or proofs, which show the whole ledger's structure.
  • A token lasts 60 to 86,400 seconds, never past the ledger's expiry. Neither scope allows exports, sealing, retention changes, erasure, token issuance, or access to stored files.

Your key pays for the token's requests. Revoke a token with DELETE /meta/client_tokens/{token_id}. It also stops at once if you revoke the key that issued it, or the key loses Audit logs. Token issuance and successful token requests each cost one work unit, plus any append and retention charges. Identical idempotent replays aren't charged again.

Proofs

Each ledger uses the Merkle tree algorithms from RFC 9162. A leaf is SHA-256(0x00 || envelope), where the envelope is the event's exact canonical bytes; leaf index i holds sequence i + 1. A checkpoint signs {version, ledger_id, tree_size, root_hash, issued_at, key_id}: Motor signs motor.audit.checkpoint.v1\n followed by signed_body, with ECDSA P-256 and SHA-256, and signature is the base64url of the 64 bytes of r || s. A new ledger has a signed checkpoint of size 0, and each batch that appends gets one.

Canonical bytes are motor-exact-json-v1: members sorted by the UTF-8 bytes of their names, numbers as exact decimals with an exponent (15e-1 for 1.5, 1e2 for 100), strings with only the quote, the backslash, and control characters escaped, and no whitespace. It isn't RFC 8785. You hash the bytes that Motor gives you, so you don't need to rebuild them. Use the offline verifier to check both the signature and the inclusion path:

python3 audit_verify.py inclusion --keys keys.json proof.json

Motor's verifier, audit_verify.py from GET /verifier, checks checkpoints and control receipts against a keys file that you saved, events, both proofs, and whole exports. It never sends a network request. It needs Python 3 and the cryptography package.

These checks prove that Motor recorded the bytes in that order and that the newer checkpoint extends the history in your saved checkpoint. They don’t prove that the recorded claims are true or that Motor showed everyone the same history. A compromised signing key could sign another history, so keep your checkpoints and compare them over time. Motor keeps old public keys available after rotation and publishes recorded compromise notices in GET /keys with compromised_at.

GET/audit_logs/ledgers/{ledger_id}/checkpoints/latest

Get the latest checkpoint

Returns the newest signed checkpoint. An empty ledger has its signed size-zero checkpoint. Costs one work unit. Credentials: a secret key, or a read token without a run.

ParameterLocationDescription
ledger_idpath
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200The checkpoint.

{
  "object": "audit_checkpoint",
  "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
  "tree_size": "4",
  "root_hash": "e08d9bbf3c09cbf9d5bb50746cc83bdea40554a41335f12de0308b9269754407",
  "issued_at": "2026-10-18T09:00:03.000000Z",
  "key_id": "development-1",
  "algorithm": "ecdsa-p256-sha256",
  "signed_body": "{\"issued_at\":\"2026-10-18T09:00:03.000000Z\",\"key_id\":\"development-1\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"root_hash\":\"e08d9bbf3c09cbf9d5bb50746cc83bdea40554a41335f12de0308b9269754407\",\"tree_size\":\"4\",\"version\":\"motor-audit-checkpoint-v1\"}",
  "signature": "RDCKx2NJ7m1EHKF3RDihL62etRo_98qTbLcv5tOuNOzCc5GEKHA0mtcGUkA-zcq9ahIkND10RQg8Vkti0FveGA",
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

410The ledger's paid retention ended: ledger_expired. Read the ledger for its deletion status.

{
  "error": {
    "code": "ledger_expired",
    "message": "The ledger's paid retention ended at 2026-11-17T09:00:00.000Z, so its events can't be read, proven, exported, or appended. Read the ledger for its deletion status.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

GET/audit_logs/ledgers/{ledger_id}/proofs/inclusion

Prove an event's inclusion

Returns the event's leaf hash, its audit path, and the signed checkpoint of tree_size, which must be a committed checkpoint that covers the event. Motor verifies the proof before it answers. Costs one work unit. Credentials: a secret key, or a read token without a run.

ParameterLocationDescription
ledger_idpath
sequencequeryThe event's sequence.
tree_sizequeryA tree size of a signed checkpoint, such as an append receipt's.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200The inclusion proof.

{
  "object": "audit_inclusion_proof",
  "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
  "event_id": "aev_6edPveaagHj5aBDcfdUsps",
  "sequence": "2",
  "leaf_index": "1",
  "tree_size": "4",
  "leaf_hash": "fd179e7caf6945129e781dd0f960b4c185f6922826e9140110d71b8270a1ff2d",
  "path": [
    "252db5297b35fac9da498ad9ad14cbf79454e2512065ead115861a080766b6ac",
    "d6b3b31c76acbfc2bb77d728f76b3e9c4c9183d4d2cd3990135167d846997d97"
  ],
  "checkpoint": {
    "tree_size": "4",
    "root_hash": "e08d9bbf3c09cbf9d5bb50746cc83bdea40554a41335f12de0308b9269754407",
    "issued_at": "2026-10-18T09:00:03.000000Z",
    "key_id": "development-1",
    "algorithm": "ecdsa-p256-sha256",
    "signed_body": "{\"issued_at\":\"2026-10-18T09:00:03.000000Z\",\"key_id\":\"development-1\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"root_hash\":\"e08d9bbf3c09cbf9d5bb50746cc83bdea40554a41335f12de0308b9269754407\",\"tree_size\":\"4\",\"version\":\"motor-audit-checkpoint-v1\"}",
    "signature": "RDCKx2NJ7m1EHKF3RDihL62etRo_98qTbLcv5tOuNOzCc5GEKHA0mtcGUkA-zcq9ahIkND10RQg8Vkti0FveGA"
  },
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

410The ledger's paid retention ended: ledger_expired. Read the ledger for its deletion status.

{
  "error": {
    "code": "ledger_expired",
    "message": "The ledger's paid retention ended at 2026-11-17T09:00:00.000Z, so its events can't be read, proven, exported, or appended. Read the ledger for its deletion status.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

GET/audit_logs/ledgers/{ledger_id}/proofs/consistency

Prove a ledger's consistency

Returns the proof that the tree of from_size is a prefix of the tree of to_size, and both signed checkpoints. Both sizes must be committed checkpoints; size zero is valid. Costs one work unit. Credentials: a secret key, or a read token without a run.

ParameterLocationDescription
ledger_idpath
from_sizequeryThe older tree size.
to_sizequeryThe newer tree size.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200The consistency proof.

{
  "object": "audit_consistency_proof",
  "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
  "from_size": "3",
  "to_size": "4",
  "proof": [
    "1d5a3c0a0981448737b02c141395c0b18d492fd6445787f253910017a13dde63",
    "32b5178fb60f75feadc8eb0d7d13eef0a169c31266a957b0e2783a5c64b3e27c",
    "e5c4af99d8f9105bd9762e6fb83e1d2147d7b284b13da2d0627f0e480e580a39"
  ],
  "from_checkpoint": {
    "tree_size": "3",
    "root_hash": "f3744b374cda1f2e104b47abf8f69e6a8469b1903fb9d63fd92c6a4894098d83",
    "issued_at": "2026-10-18T09:00:02.000000Z",
    "key_id": "development-1",
    "algorithm": "ecdsa-p256-sha256",
    "signed_body": "{\"issued_at\":\"2026-10-18T09:00:02.000000Z\",\"key_id\":\"development-1\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"root_hash\":\"f3744b374cda1f2e104b47abf8f69e6a8469b1903fb9d63fd92c6a4894098d83\",\"tree_size\":\"3\",\"version\":\"motor-audit-checkpoint-v1\"}",
    "signature": "RDCKx2NJ7m1EHKF3RDihL62etRo_98qTbLcv5tOuNOzCc5GEKHA0mtcGUkA-zcq9ahIkND10RQg8Vkti0FveGA"
  },
  "to_checkpoint": {
    "tree_size": "4",
    "root_hash": "e08d9bbf3c09cbf9d5bb50746cc83bdea40554a41335f12de0308b9269754407",
    "issued_at": "2026-10-18T09:00:03.000000Z",
    "key_id": "development-1",
    "algorithm": "ecdsa-p256-sha256",
    "signed_body": "{\"issued_at\":\"2026-10-18T09:00:03.000000Z\",\"key_id\":\"development-1\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"root_hash\":\"e08d9bbf3c09cbf9d5bb50746cc83bdea40554a41335f12de0308b9269754407\",\"tree_size\":\"4\",\"version\":\"motor-audit-checkpoint-v1\"}",
    "signature": "RDCKx2NJ7m1EHKF3RDihL62etRo_98qTbLcv5tOuNOzCc5GEKHA0mtcGUkA-zcq9ahIkND10RQg8Vkti0FveGA"
  },
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

410The ledger's paid retention ended: ledger_expired. Read the ledger for its deletion status.

{
  "error": {
    "code": "ledger_expired",
    "message": "The ledger's paid retention ended at 2026-11-17T09:00:00.000Z, so its events can't be read, proven, exported, or appended. Read the ledger for its deletion status.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

GET/audit_logs/keys

List the public signing keys

Returns every public key that ever signed a checkpoint or a control receipt, with its status and any compromise notice. It doesn't need a credential and it's free, so verifiers can fetch it without an account. Signatures under a compromised key may be untrustworthy.

This endpoint authenticates with the secret in its URL. Keep the URL private.

Responses

200The public keys.

{
  "object": "audit_signing_keys",
  "data": [
    {
      "key_id": "development-1",
      "algorithm": "ecdsa-p256-sha256",
      "public_key": "BOq_0Q-Pa7o46XwtfLCle4nFPCnHodhWlzROQCJi14sFQ3O2iHMYCbOYK2yU65yKrTUgsZFHgoWJYLOu5HB3SrI",
      "public_key_encoding": "sec1_uncompressed_base64url",
      "status": "active",
      "created_at": "2026-10-18T09:00:00.000000Z",
      "retired_at": null,
      "compromised_at": null
    }
  ],
  "checkpoint_prefix": "motor.audit.checkpoint.v1\n",
  "control_prefix": "motor.audit.control.v1\n",
  "signature_encoding": "base64url of the 64 bytes of r || s",
  "canonicalization": "motor-exact-json-v1"
}

GET/audit_logs/verifier

Download the offline verifier

Returns audit_verify.py, Motor's offline verifier of checkpoints, controls, canonical bytes, inclusion and consistency proofs, and exports. It needs Python 3 and the cryptography package, and it doesn't make network requests. Give it a public key file from GET /keys that you trust. It doesn't need a credential, and it's free.

This endpoint authenticates with the secret in its URL. Keep the URL private.

Responses

200The verifier script.

503Motor can't sign checkpoints (signing_unavailable), a ledger is under an integrity check (integrity_check), or the billing month is closing. Retry later.

{
  "error": {
    "code": "signing_unavailable",
    "message": "Motor can't sign checkpoints right now, so it doesn't accept new events. Motor didn't append or charge anything. Retry later.",
    "retryable": true,
    "retry_after_seconds": 60
  }
}

Evidence

An event can reference at most 8 pieces of evidence. Motor never reads their bytes or fetches a URI.

  • {"artifact_id": "art_...", "mode": "pointer"}: records the artifact storage artifact's verified full-object SHA-256 (or digest_status: "unavailable" for a multipart upload) and its size. It doesn’t prevent deletion or expiry, so the artifact may become unavailable.
  • {"artifact_id": "art_...", "mode": "retain"}: also keeps the artifact from deletion until the ledger expires or the last event that retains it is erased. The artifact needs a full-object SHA-256, and an expires_at that is empty or at least the ledger's expiry. A ledger retains at most 100 distinct artifacts.
  • {"uri": "https://...", "sha256": "..."}: an HTTPS URI of at most 1,024 bytes, without credentials or a fragment, and an optional lowercase hexadecimal SHA-256. Motor doesn't check it.

Artifact storage keeps billing artifacts as usual; Audit logs never changes their expiry. GET /ledgers/{ledger_id}/evidence lists retained artifacts with their state and artifact_expires_at; expires_before finds those that end too soon for an extension.

GET/audit_logs/ledgers/{ledger_id}/evidence

List retained evidence

Returns the ledger's retained artifacts with their current state and paid retention. It never returns bytes or URLs; get download URLs from Artifact storage. Costs one work unit. Credentials: a secret key.

ParameterLocationDescription
ledger_idpath
expires_beforequeryOnly artifacts whose paid retention ends before this time.
limitquery1 to 100 items, 50 by default.
cursorquerynext_cursor of the previous page. It works for 15 minutes, with the same filters and credential.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200A page of retained evidence.

{
  "data": [
    {
      "artifact_id": "art_Co8jNxhZn3uCz6ZmBLYx6w",
      "references": 1,
      "retained_until": "2026-11-17T09:00:00.000000Z",
      "artifact_status": "ready",
      "artifact_expires_at": "2027-01-15T09:00:00.000000Z",
      "created_at": "2026-10-18T09:00:02.000000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

410The ledger's paid retention ended: ledger_expired. Read the ledger for its deletion status.

{
  "error": {
    "code": "ledger_expired",
    "message": "The ledger's paid retention ended at 2026-11-17T09:00:00.000Z, so its events can't be read, proven, exported, or appended. Read the ledger for its deletion status.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

Erasure

POST /ledgers/{ledger_id}/erasures with event_ids (1 to 100) and reason_code (customer_request or sensitive_data) erases payloads. They disappear from every read at once; the returned operation tracks cleanup of their canonical bytes, salts, index entries, and evidence references. Poll it until it succeeds. Each event keeps its sequence, its random ID, and its leaf hash, so proofs still verify, and reads show an explicit tombstone. The signed control receipt names the operation, the reason code, the number of events, and the SHA-256 of the operation's event_ids, so you can check the list. Motor acknowledges an erasure only after it records it outside its database, so a restore from a backup applies it again; a refused request changes nothing. If a response is lost, retry with the same Idempotency-Key: you get the same operation, and nothing is charged or signed twice. An erasure stops older query cursors and running exports, and withdraws the artifacts of earlier exports. Copies that you already downloaded can't be recalled, and a download URL that Artifact storage already issued can work until it expires.

DELETE /ledgers/{ledger_id} with If-Match deletes a ledger: reads stop at once, its tokens and exports stop, and a background operation deletes its records and releases its evidence references. Your retained artifacts stay in artifact storage. Closing a team deletes all its ledgers. Prepaid retention isn't refunded. GET /operations/{operation_id} shows the progress. Erasure, deletion, cancellation, and revocation still work when your key is at its spending limit. Erasure, deletion, and token revocation can't be undone. Cleanup removes erased payloads from the live database. Encrypted historical database versions currently don't have a fixed deletion deadline.

POST/audit_logs/ledgers/{ledger_id}/erasures

Erase event payloads

Erases the payloads of 1 to 100 events. They disappear at once, and the returned operation tracks background cleanup; poll it until it succeeds. Each event keeps its sequence, ID, and leaf hash, so proofs still verify. Older query and export snapshots stop, and earlier exports lose their artifacts. Copies that you already have can't be recalled. Prepaid retention isn't refunded. Costs one work unit when the key can pay; otherwise the erasure is included. Credentials: a secret key. Erasure is irreversible and removes data from the live service. Encrypted historical database versions currently don't have a fixed deletion deadline. Download URLs already issued may remain usable until they expire; downloaded copies can't be recalled.

ParameterLocationDescription
ledger_idpath
Idempotency-Keyheader1 to 128 printable ASCII characters: a new value for each operation, and the same value when you retry it. A retry with the same credential replays the first response for 35 days.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Request body
{
  "event_ids": [
    "aev_4hya3wkMnJMhbAKFeiScRN"
  ],
  "reason_code": "sensitive_data"
}
FieldTypeDescription
event_idsrequiredarray
reason_coderequired
Responses

202The erasure's operation and its control receipt.

{
  "operation": {
    "id": "aop_N3d27GEhQsEkL4hmM72ZnZ",
    "object": "audit_operation",
    "kind": "erasure",
    "state": "pending",
    "reason_code": "sensitive_data",
    "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
    "event_ids": [
      "aev_4hya3wkMnJMhbAKFeiScRN"
    ],
    "phase": "events",
    "counts": {
      "events": "1",
      "erased": "0",
      "removed_rows": "0"
    },
    "retry": {
      "attempts": 0,
      "error_code": null
    },
    "created_at": "2026-10-18T09:00:45.000000Z",
    "updated_at": "2026-10-18T09:00:45.000000Z",
    "finished_at": null,
    "deadline_at": "2026-10-19T09:00:45.000000Z"
  },
  "control_receipt": {
    "kind": "erasure",
    "index": 2,
    "issued_at": "2026-10-18T09:00:10.000000Z",
    "key_id": "development-1",
    "algorithm": "ecdsa-p256-sha256",
    "signed_body": "{\"event_ids\":[\"aev_4hya3wkMnJMhbAKFeiScRN\"],\"expires_at_after\":\"2026-11-17T09:00:00.000000Z\",\"expires_at_before\":\"2026-11-17T09:00:00.000000Z\",\"index\":\"2\",\"issued_at\":\"2026-10-18T09:00:10.000000Z\",\"key_id\":\"development-1\",\"kind\":\"erasure\",\"ledger_id\":\"ald_LrB528wYtd4M8VJwtZikAt\",\"reason_code\":\"sensitive_data\",\"revision_after\":\"1\",\"revision_before\":\"1\",\"tree_size\":\"4\",\"version\":\"motor-audit-control-v1\"}",
    "signature": "KPPDcGesEqF-QcPLWlA4sNPi_0ZIf81Rv-t2rQpcpu_hrx88t0AoYKlJcG_pjnOURMwM6l3yyPb7i9ULKZz9xA"
  },
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

410The ledger's paid retention ended: ledger_expired. Read the ledger for its deletion status.

{
  "error": {
    "code": "ledger_expired",
    "message": "The ledger's paid retention ended at 2026-11-17T09:00:00.000Z, so its events can't be read, proven, exported, or appended. Read the ledger for its deletion status.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

400The body isn't a JSON object, or an Idempotency-Key is missing.

{
  "error": {
    "code": "invalid_json",
    "message": "The request body isn't valid JSON. Expected a JSON value at byte 0.",
    "retryable": false
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

409A conflict. Codes: ledger_sealed, event_conflict, evidence_expiring, evidence_unavailable, snapshot_invalidated, capacity_exceeded, idempotency_conflict, request_in_progress.

{
  "error": {
    "code": "ledger_sealed",
    "message": "The ledger is sealed, so it accepts no more events. Create another ledger for new events.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

413The body is too large: 512 KiB for an append, and 64 KiB otherwise.

{
  "error": {
    "code": "body_too_large",
    "message": "The request body must be at most 524288 bytes.",
    "retryable": false
  }
}

415The body isn't application/json.

{
  "error": {
    "code": "unsupported_media_type",
    "message": "Send the request body as application/json.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

503Motor can't sign checkpoints (signing_unavailable), a ledger is under an integrity check (integrity_check), or the billing month is closing. Retry later.

{
  "error": {
    "code": "signing_unavailable",
    "message": "Motor can't sign checkpoints right now, so it doesn't accept new events. Motor didn't append or charge anything. Retry later.",
    "retryable": true,
    "retry_after_seconds": 60
  }
}

GET/audit_logs/operations/{operation_id}

Get an operation

Returns the progress of an erasure or a deletion. Costs one work unit when the key can pay; otherwise the read is included. Credentials: a secret key.

ParameterLocationDescription
operation_idpath
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200The operation.

{
  "id": "aop_N3d27GEhQsEkL4hmM72ZnZ",
  "object": "audit_operation",
  "kind": "erasure",
  "state": "succeeded",
  "reason_code": "sensitive_data",
  "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
  "event_ids": [
    "aev_4hya3wkMnJMhbAKFeiScRN"
  ],
  "phase": "done",
  "counts": {
    "events": "1",
    "erased": "1",
    "removed_rows": "5"
  },
  "retry": {
    "attempts": 0,
    "error_code": null
  },
  "created_at": "2026-10-18T09:00:45.000000Z",
  "updated_at": "2026-10-18T09:00:47.000000Z",
  "finished_at": "2026-10-18T09:00:47.000000Z",
  "deadline_at": "2026-10-19T09:00:45.000000Z",
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

Exports

POST /ledgers/{ledger_id}/exports with max_cost_usd exports the ledger so far, or from_sequence through through_sequence, optionally with filters as used when listing events. It captures the tree size at once. An export scans at most 1,000,000 candidate events and produces at most 2,048 parts or 8 GiB. Split larger ranges by sequence. Its artifacts last artifact_retention_days (1 to 30, 1 by default), never past the ledger's expiry, and its deadline is deadline_seconds (60 to 86,400, 3,600 by default). A team runs at most 2 exports at once.

Parts are NDJSON of at most 4 MiB, with a line for every sequence of the range, in order. A matching event's line is {"sequence", "event_id", "leaf_hash", "canonical_base64"}, an erased event's line has payload_erased: true instead of its bytes, and an event that the filters leave out has only its sequence, its leaf hash, and omitted: true. The manifest lists every part's artifact ID, SHA-256, size, lines, and range, the filters and range, the signed checkpoint, and a range_proof: the subtree hashes outside the range. The lines' leaf hashes and the range proof rebuild the signed root, so audit_verify.py export authenticates every line and its sequence, in a complete or partial export, without trusting the rest of the manifest. It counts lines with content, tombstones, and hash-only lines: the root proves their leaf hashes, not that a tombstone's event was erased or that an omitted event doesn't match the filters. complete_ledger: true means every sequence from 1 to the snapshot. List parts with GET /exports/{export_id}/parts, and download them through artifact storage. The export succeeds only after every part and the manifest are ready.

Each chunk of up to 1,000 candidates, and the manifest, is one work unit, and artifact storage charges a write for each part and the manifest. The response'squote_usd is the most that the export could cost. Artifact storage then meters the storage of the parts and the manifest to the export's key until they expire, with a 128 KiB minimum per artifact. The export never exceeds max_cost_usd: it stops with max_cost_exceeded and keeps the parts that committed. POST /exports/{export_id}/cancel stops later steps. Storage and downloads incur artifact storage charges separately; those charges aren't covered by the export's quote or cost cap.

POST/audit_logs/ledgers/{ledger_id}/exports

Export a ledger

Exports a range of the ledger's snapshot as NDJSON parts of at most 4 MiB and a manifest, each an artifact in Artifact storage. Every sequence of the range has a line with its leaf hash: a matching event's canonical bytes in Base64, a tombstone with payload_erased: true, or omitted: true for an event that the filters leave out. The manifest's range_proof lists the subtree hashes outside the range; with the lines' leaf hashes, it rebuilds the root of the signed checkpoint, which authenticates every line and its sequence. That doesn't prove that a tombstone's event was erased, or that an omitted event doesn't match the filters. Each step examines at most 1,000 candidates and costs one work unit, and Artifact storage charges for writes and prepaid retention of each part and the manifest. Downloads incur separate read and delivery charges outside the export cost cap. The export never exceeds max_cost_usd. Limits: 1,000,000 candidates, 8 GiB, and 2,048 parts. Costs one work unit at admission. Credentials: a secret key.

ParameterLocationDescription
ledger_idpath
Idempotency-Keyheader1 to 128 printable ASCII characters: a new value for each operation, and the same value when you retry it. A retry with the same credential replays the first response for 35 days.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Request body
{
  "from_sequence": "1",
  "through_sequence": "4",
  "artifact_retention_days": 2,
  "deadline_seconds": 3600,
  "max_cost_usd": "1"
}
FieldTypeDescription
from_sequencestringAn unsigned integer returned as a decimal string, such as 1024, to avoid loss of precision in JSON clients.
through_sequencestringAn unsigned integer returned as a decimal string, such as 1024, to avoid loss of precision in JSON clients.
filtersobjectThe same filters used to list events. Send actor_type with actor_id, and target_type with target_id.
artifact_retention_daysinteger1 to 30, 1 by default. It can't outlast the ledger's expiry.
max_cost_usdrequiredstringRequired: the most that the export's work and artifacts may cost.
deadline_secondsinteger60 to 86,400, 3,600 by default.
Responses

202The queued export and its cost bound.

{
  "export": {
    "id": "aex_Jm7CidaYcuEm2rbDR4wQE3",
    "object": "audit_export",
    "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
    "state": "queued",
    "from_sequence": "1",
    "through_sequence": "4",
    "snapshot_tree_size": "4",
    "filters": {
      "indexed": [],
      "occurred_before": null,
      "occurred_from": null,
      "outcome": null,
      "received_before": null,
      "received_from": null,
      "source_kind": null
    },
    "complete_ledger": true,
    "artifact_retention_days": "2",
    "deadline_at": "2026-10-18T10:00:11.000000Z",
    "progress": {
      "last_examined_sequence": "0",
      "scanned": "0",
      "matched": "0",
      "parts": 0,
      "bytes": "0",
      "phase": "scan"
    },
    "manifest_artifact_id": null,
    "cost": {
      "rate_version": "2026-10-18",
      "max_cost_usd": "1.00",
      "spent_usd": "0.001",
      "next_step_reserved_usd": "0.001",
      "work_units": "1"
    },
    "error": null,
    "cancel_requested": false,
    "withdrawn_at": null,
    "created_at": "2026-10-18T09:00:11.000000Z",
    "finished_at": null
  },
  "quote_usd": "2.104652677739",
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

410The ledger's paid retention ended: ledger_expired. Read the ledger for its deletion status.

{
  "error": {
    "code": "ledger_expired",
    "message": "The ledger's paid retention ended at 2026-11-17T09:00:00.000Z, so its events can't be read, proven, exported, or appended. Read the ledger for its deletion status.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

400The body isn't a JSON object, or an Idempotency-Key is missing.

{
  "error": {
    "code": "invalid_json",
    "message": "The request body isn't valid JSON. Expected a JSON value at byte 0.",
    "retryable": false
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

409A conflict. Codes: ledger_sealed, event_conflict, evidence_expiring, evidence_unavailable, snapshot_invalidated, capacity_exceeded, idempotency_conflict, request_in_progress.

{
  "error": {
    "code": "ledger_sealed",
    "message": "The ledger is sealed, so it accepts no more events. Create another ledger for new events.",
    "retryable": false,
    "resource_id": "ald_LrB528wYtd4M8VJwtZikAt"
  }
}

413The body is too large: 512 KiB for an append, and 64 KiB otherwise.

{
  "error": {
    "code": "body_too_large",
    "message": "The request body must be at most 524288 bytes.",
    "retryable": false
  }
}

415The body isn't application/json.

{
  "error": {
    "code": "unsupported_media_type",
    "message": "Send the request body as application/json.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

503Motor can't sign checkpoints (signing_unavailable), a ledger is under an integrity check (integrity_check), or the billing month is closing. Retry later.

{
  "error": {
    "code": "signing_unavailable",
    "message": "Motor can't sign checkpoints right now, so it doesn't accept new events. Motor didn't append or charge anything. Retry later.",
    "retryable": true,
    "retry_after_seconds": 60
  }
}

GET/audit_logs/exports

List exports

Returns the team's exports in creation order. Costs one work unit. Credentials: a secret key.

ParameterLocationDescription
statequery
limitquery1 to 100 items, 50 by default.
cursorquerynext_cursor of the previous page. It works for 15 minutes, with the same filters and credential.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200A page of exports.

{
  "data": [
    {
      "id": "aex_Jm7CidaYcuEm2rbDR4wQE3",
      "object": "audit_export",
      "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
      "state": "succeeded",
      "from_sequence": "1",
      "through_sequence": "4",
      "snapshot_tree_size": "4",
      "filters": {
        "indexed": [],
        "occurred_before": null,
        "occurred_from": null,
        "outcome": null,
        "received_before": null,
        "received_from": null,
        "source_kind": null
      },
      "complete_ledger": true,
      "artifact_retention_days": "2",
      "deadline_at": "2026-10-18T10:00:11.000000Z",
      "progress": {
        "last_examined_sequence": "4",
        "scanned": "4",
        "matched": "4",
        "parts": 1,
        "bytes": "4956",
        "phase": "done"
      },
      "manifest_artifact_id": "art_CgCD4iV1RVTfPqQTSbepAm",
      "cost": {
        "rate_version": "2026-10-18",
        "max_cost_usd": "1.00",
        "spent_usd": "0.003030699052",
        "next_step_reserved_usd": "0.00",
        "work_units": "3"
      },
      "error": null,
      "cancel_requested": false,
      "withdrawn_at": null,
      "created_at": "2026-10-18T09:00:11.000000Z",
      "finished_at": "2026-10-18T09:00:30.000000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

GET/audit_logs/exports/{export_id}

Get an export

Returns the export's state, progress, and cost. Only a succeeded export has a manifest; its committed parts stay until their artifacts expire. Costs one work unit. Credentials: a secret key.

ParameterLocationDescription
export_idpath
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200The export.

{
  "id": "aex_Jm7CidaYcuEm2rbDR4wQE3",
  "object": "audit_export",
  "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
  "state": "succeeded",
  "from_sequence": "1",
  "through_sequence": "4",
  "snapshot_tree_size": "4",
  "filters": {
    "indexed": [],
    "occurred_before": null,
    "occurred_from": null,
    "outcome": null,
    "received_before": null,
    "received_from": null,
    "source_kind": null
  },
  "complete_ledger": true,
  "artifact_retention_days": "2",
  "deadline_at": "2026-10-18T10:00:11.000000Z",
  "progress": {
    "last_examined_sequence": "4",
    "scanned": "4",
    "matched": "4",
    "parts": 1,
    "bytes": "4956",
    "phase": "done"
  },
  "manifest_artifact_id": "art_CgCD4iV1RVTfPqQTSbepAm",
  "cost": {
    "rate_version": "2026-10-18",
    "max_cost_usd": "1.00",
    "spent_usd": "0.003030699052",
    "next_step_reserved_usd": "0.00",
    "work_units": "3"
  },
  "error": null,
  "cancel_requested": false,
  "withdrawn_at": null,
  "created_at": "2026-10-18T09:00:11.000000Z",
  "finished_at": "2026-10-18T09:00:30.000000Z",
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

GET/audit_logs/exports/{export_id}/parts

List an export's parts

Returns a page of the export's parts: their artifact IDs, SHA-256 checksums, sizes, and ranges. Get download URLs from Artifact storage. Costs one work unit. Credentials: a secret key.

ParameterLocationDescription
export_idpath
limitquery1 to 100 items, 50 by default.
cursorquerynext_cursor of the previous page. It works for 15 minutes, with the same filters and credential.
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Responses

200A page of parts.

{
  "data": [
    {
      "ordinal": 0,
      "artifact_id": "art_4QhaL5RPcXmVDSkqjWkeBH",
      "sha256": "3d431bd5b2ed25ea3dcea37c86a47a4a40ea15c872b7733e3d92b857bf303960",
      "size_bytes": "4956",
      "lines": 4,
      "first_sequence": "1",
      "last_sequence": "4",
      "state": "ready"
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "manifest_artifact_id": "art_CgCD4iV1RVTfPqQTSbepAm",
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

402The team doesn't have a payment card, the key or the token reached its spending limit, or the work would pass max_cost_usd. Codes: billing_required, spending_limit, max_cost_exceeded, token_budget_exceeded.

{
  "error": {
    "code": "max_cost_exceeded",
    "message": "This operation costs 0.002048576 USD, more than max_cost_usd allows (0.0001 USD). Raise max_cost_usd, or ask for less.",
    "retryable": false,
    "cost_usd": "0.002048576"
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}

POST/audit_logs/exports/{export_id}/cancel

Cancel an export

Stops the export's future work. A step that already started is paid, and committed parts stay until they expire. Canceling a finished export answers with its state. Costs one work unit when the key can pay; otherwise it's included. Credentials: a secret key. Cancellation doesn't replay responses: each successful request costs a work unit when the key can pay, even if the export already finished.

ParameterLocationDescription
export_idpath
Motor-ModeheaderAudit logs is live-only: omit it or send live.
Request body
{}
Responses

202The export.

{
  "export": {
    "id": "aex_Jm7CidaYcuEm2rbDR4wQE3",
    "object": "audit_export",
    "ledger_id": "ald_LrB528wYtd4M8VJwtZikAt",
    "state": "canceled",
    "from_sequence": "1",
    "through_sequence": "4",
    "snapshot_tree_size": "4",
    "filters": {
      "indexed": [],
      "occurred_before": null,
      "occurred_from": null,
      "outcome": null,
      "received_before": null,
      "received_from": null,
      "source_kind": null
    },
    "complete_ledger": true,
    "artifact_retention_days": "2",
    "deadline_at": "2026-10-18T10:00:11.000000Z",
    "progress": {
      "last_examined_sequence": "4",
      "scanned": "4",
      "matched": "4",
      "parts": 1,
      "bytes": "4956",
      "phase": "done"
    },
    "manifest_artifact_id": null,
    "cost": {
      "rate_version": "2026-10-18",
      "max_cost_usd": "1.00",
      "spent_usd": "0.003030699052",
      "next_step_reserved_usd": "0.00",
      "work_units": "3"
    },
    "error": {
      "code": "canceled",
      "message": "The export was canceled.",
      "retryable": false
    },
    "cancel_requested": true,
    "withdrawn_at": null,
    "created_at": "2026-10-18T09:00:11.000000Z",
    "finished_at": "2026-10-18T09:00:30.000000Z"
  },
  "usage": {
    "rate_version": "2026-10-18",
    "work_units": "1",
    "new_events": "0",
    "retained_byte_days": "0",
    "charged_usd": "0.001"
  }
}

400The body isn't a JSON object, or an Idempotency-Key is missing.

{
  "error": {
    "code": "invalid_json",
    "message": "The request body isn't valid JSON. Expected a JSON value at byte 0.",
    "retryable": false
  }
}

401The key or the client token is missing, invalid, expired, or revoked.

{
  "error": {
    "code": "unauthorized",
    "message": "This API key is invalid or has been revoked.",
    "retryable": false
  }
}

403The credential can't do this: a publishable key, a key without Audit logs, a client token outside its grant, or a suspended team.

{
  "error": {
    "code": "forbidden",
    "message": "Creating a ledger requires a secret API key (motor_sk_...) of the team, not a client token.",
    "retryable": false
  }
}

404The resource doesn't exist for your team or your token's grant. A missing checkpoint is checkpoint_not_found.

{
  "error": {
    "code": "not_found",
    "message": "The ledger doesn't exist.",
    "retryable": false
  }
}

415The body isn't application/json.

{
  "error": {
    "code": "unsupported_media_type",
    "message": "Send the request body as application/json.",
    "retryable": false
  }
}

422A field, a query parameter, the cursor (invalid_cursor), or Motor-Mode (unsupported_mode) isn't valid.

{
  "error": {
    "code": "invalid_field",
    "message": "`events[0].outcome` must be one of attempted, succeeded, failed, denied, unknown.",
    "retryable": false
  }
}

429Too many requests: the team's requests or appended events, or 2 feed reads a second for each ledger. Retry after Retry-After.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 1 seconds.",
    "retryable": true,
    "retry_after_seconds": 1
  }
}