Docs

Motor's REST API is at /api. People and their agents use it to manage teams, register agents, read usage, listen to what happens, and run code in Piston. Every endpoint is in an OpenAPI 3.1 spec.

APIs

3 available

Getting started

  1. Sign up, make a passkey, and add a card.
  2. Run motor auth. It opens this site, you approve with your passkey, and the CLI gets a session key for 24 hours, bound to your device.
  3. Register an agent: motor agent start --team team_… --name reviewer --scope "current:*". The CLI prints the agent's token. Use motor agent token <agent> to get a new token before it expires.

Or, with the API and a session key:

curl -X POST https://new.motor.dev/api/agents \
  -H "Authorization: Bearer $MOTOR_SESSION_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"team_id": "team_…", "name": "reviewer", "scopes": ["current:*"]}'

Credentials

Send a credential in the Authorization header. There are three kinds:

CredentialPrefixLastsFor
Session keymotor_sk_24 hoursDay-to-day work. motor auth gets one with your approval in the browser; a user key mints one with POST /api/sessions. It acts as you, registers agents, and renews their tokens, but it can't manage keys.
User keymotor_uk_Until it expiresAutomation, such as CI, where nobody can approve in a browser. You make one in the console. It manages keys and mints session keys; it can't register agents.
Agent tokenmotor_tk_At most 4 hoursAn agent, in one team, with the agent's scopes. The session key that registered the agent, or a parent agent, gives it a new token with POST /api/agents/{agent_id}/tokens. An agent can't renew its own token.

Revoking a key revokes the session keys that it minted and deregisters their agents. Deregistering an agent deregisters its sub-agents and revokes their tokens.

Session keys bound to a device

motor auth binds its session key to a key of your device: in the TPM, where the device has one, and in the keychain otherwise. A bound session key works only with a fresh proof that the device's key signs (DPoP, RFC 9449):

Authorization: DPoP motor_sk_…
DPoP: <a JWT that the device key signs, with the method, the URL, the time, and the hash of the key>

Each proof works once, for one minute. A session key that leaks, such as into a log, is useless without the device. The Motor CLI makes the proofs; to bind a key that you mint with a user key, send a proof with POST /api/sessions.

Scopes

An agent may do what its scopes cover. A scope is <area>:<action>, such as current:write; current:* covers every action of an area, and * covers everything. Each service checks the scopes of its own area. Motor's, with Piston's, whose API Motor serves, are:

ScopeAllows
agents:startRegister sub-agents, and renew their tokens. A sub-agent's scopes, lifetime, and spending limit are at most its parent's.
events:readRead and stream the team's events.
team:readRead the team and its members.
usage:readRead the team's usage.
piston:runUpload sources, check plans, start and cancel runs, and delete volumes in Piston.
piston:readRead Piston's runs, logs, outputs, and volumes.
piston:secretsList, set, and delete the team's secrets in Piston. Nobody reads a secret's value back.

Conventions

Requests and responses

  • Bodies are JSON objects, sent as application/json, of at most 1 MiB. Motor refuses fields that it doesn't know.
  • IDs are a prefix and 22 characters, such as team_1BbA6sXxyUWHYvKqN5T3dM. IDs of one kind sort by when Motor made them.
  • Times are RFC 3339 in UTC, with milliseconds, such as 2026-10-22T09:00:00.250Z.
  • Money is in whole cents, in fields that end in _cents.
  • In a PATCH, a missing field keeps its value, and null clears it.
  • Each response has a Request-Id header. Give it to support with a question.

Pagination

A list answers {"data": […], "next_cursor": …}. Pass limit (1 to 100, 50 if you don't) and, for the next page, cursor with the value of next_cursor, which is null on the last page.

Idempotency

Send an Idempotency-Key header, 1 to 128 printable characters, with each request that makes something, such as POST /api/agents. For 24 hours, the same key and the same request get the first answer again, with Idempotency-Replayed: true, so a client can retry without making two agents. The same key with another request is a 409 idempotency_conflict. Motor seals the stored answer with your credential, so only you can see it again.

Rate limits

Each credential may make 600 requests a minute. Past that, Motor answers 429 rate_limited with a Retry-After header.

Errors

An error answers with its status and a body like this:

{"error": {"code": "invalid_field", "message": "`name` is required.", "field": "name", "retryable": false}}
StatusCodes
400invalid_json, and in the CLI's sign-in: authorization_pending, slow_down, access_denied, invalid_grant
401unauthorized, dpop_required, invalid_dpop_proof
402billing_required: the team needs a card; over_limit: the team or the agent spent its spending limit for the month
403forbidden, insufficient_scope, session_key_required, user_key_required, account_suspended, team_suspended
404not_found
409conflict, idempotency_conflict, request_in_progress
413, 415body_too_large, unsupported_media_type
422invalid_field, and in Piston: invalid_plan, with each problem of the config in diagnostics
429rate_limited
500, 501, 503internal_error, not_implemented, temporarily_unavailable, unavailable

When retryable is true, try again after retry_after_seconds, if the body has it.

Billing

Every service bills the five meters on the home page. A team pays for what its people and agents use, at the start of each month; a personal team gets $10 a month for free. A team and each agent can have a spending limit: when one is reached, the services stop starting new work for it until the next month.