Agents API reference

Updated

6 operations of the Fax for Agents API, from the OpenAPI document version 1.20.0, snapshot of 2026-11-13. The base URL is https://api.faxforagents.com/v1. This is one group of the full reference.

  • Setting enabled to false on an agent revokes every key of that agent.
  • An agent key is never issued approval:decide, key:admin, policy:admin or agent:admin.
  • The purpose of an agent is shown to people and is never evaluated by the server.

Operations

What is an agent identity in the API?

An agent identity is a named principal inside your organization. It has a name, a purpose written for people, a contact address, a list of policies and a status. A fax sent with one of its keys is recorded against the agent. The audit trail then says which agent acted, not only which organization.

Registering an agent does not let it send anything. POST /v1/agents writes the identity and answers with its id. The agent can call the API only after a key is minted for it. The read of one agent returns the identity with its keys and its budgets in one answer, so a dashboard needs one call per agent.

How does an agent get its key?

POST /v1/agents/{agent_id}/keys mints a key that belongs to the agent. The body can name a label, an environment, a number of days until it expires and a list of scopes. Leave the scopes out and the key is given the default agent set. The answer carries the secret once. Later reads show the prefix, the scopes and the dates, never the secret.

The scopes are bounded. The schema of the request says an agent key is never issued approval:decide, key:admin, policy:admin or agent:admin. A request that asks for one of them is refused. That boundary is why an agent can ask a person to approve a send and cannot approve its own request. The agents guide lists the full default set and the scopes that are never issued.

What happens when an agent is disabled?

PATCH /v1/agents/{agent_id} changes the name, the purpose, the contact address and the policy list. It also takes enabled. Sending false disables the agent and revokes each of its keys in the same call, so none of its keys outlives the identity.

Disabling is the fast way to stop one agent without touching the others. Keys of other agents and keys of the organization keep working.

Where do I see what an agent did?

GET /v1/agents/{agent_id}/activity returns the audit rows of one agent, a page at a time. It takes cursor and limit, and each page carries next_cursor while more rows remain. The rows hold metadata about each action. They never hold the content of a document.

Which scopes do these calls need?

Reading the list, one agent or its activity needs agent:read. Registering an agent, changing it and minting a key for it need agent:admin. Each operation below states its own scope on its credential line, read from the OpenAPI document.

Agent keys cannot run the admin calls of this group. A person does that from a signed-in session, or a key of the organization that holds agent:admin does it from a script. The list call returns every agent of the organization in the environment of the credential.

How do agents relate to policies and budgets?

An agent names its policies in policy_ids. A delegated policy says where the agent may send and how much, and the server evaluates it on each send. A budget is a spending cap. The read of an agent returns its budgets. The cap itself is created in the billing group with the agent's id.

The three together bound one agent. Its scopes say which calls it may make. The policy says which sends are allowed without a person. The budget says how much it may spend.

Each operation in detail

GET /v1/agents

List agent identities

Credential: an API key or a signed-in session. Scope: agent:read.

Responses

GET /v1/agents responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

GET /v1/agents response fields
FieldTypeRequiredDescription
itemsarray of object Agentyes

Example

curl https://api.faxforagents.com/v1/agents \
  -H "Authorization: Bearer ffa_test_..."

POST /v1/agents

Register an agent identity

Credential: an API key or a signed-in session. Scope: agent:admin.

Parameters

POST /v1/agents parameters
NameInTypeRequiredNotes
Idempotency-KeyheaderstringnoReplay the first successful response for the same request within 24 hours. Reusing the idempotency header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay.

Request body application/json, required

POST /v1/agents request body
FieldTypeRequiredDescription
contact_emailstring or nullnoWhere decided approvals are mailed.
namestringyes
policy_idsarray of stringno
purposestring or nullnoShown to humans, never evaluated.

Responses

POST /v1/agents responses
StatusMeaning
201Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
404not_found: no such resource for this credential. A resource of another organization answers the same.
409The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

POST /v1/agents response fields
FieldTypeRequiredDescription
budget_idstring or nullyes
budgetsarray of objectyes
contact_emailstring or nullyes
created_atstringyes
created_bystringyes
disabled_atstring or nullyes
enabledbooleanyes
idstringyes
keysarray of object ApiKeyyes
namestringyes
policy_idsarray of stringyes
project_idstring or nullyes
purposestring or nullyes
statusstringyes
updated_atstringyes

Example

curl https://api.faxforagents.com/v1/agents \
  -X POST \
  -H "Authorization: Bearer ffa_test_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"example"}'

GET /v1/agents/{agent_id}

Read an agent with its keys and budgets

Credential: an API key or a signed-in session. Scope: agent:read.

Parameters

GET /v1/agents/{agent_id} parameters
NameInTypeRequiredNotes
agent_idpathstringyes

Responses

GET /v1/agents/{agent_id} responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
404not_found: no such resource for this credential. A resource of another organization answers the same.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

GET /v1/agents/{agent_id} response fields
FieldTypeRequiredDescription
budget_idstring or nullyes
budgetsarray of objectyes
contact_emailstring or nullyes
created_atstringyes
created_bystringyes
disabled_atstring or nullyes
enabledbooleanyes
idstringyes
keysarray of object ApiKeyyes
namestringyes
policy_idsarray of stringyes
project_idstring or nullyes
purposestring or nullyes
statusstringyes
updated_atstringyes

Example

curl https://api.faxforagents.com/v1/agents/AGENT_ID \
  -H "Authorization: Bearer ffa_test_..."

PATCH /v1/agents/{agent_id}

Change an agent; disabling revokes its keys

Credential: an API key or a signed-in session. Scope: agent:admin.

Parameters

PATCH /v1/agents/{agent_id} parameters
NameInTypeRequiredNotes
agent_idpathstringyes
Idempotency-KeyheaderstringnoReplay the first successful response for the same request within 24 hours. Reusing the idempotency header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay.

Request body application/json, required

PATCH /v1/agents/{agent_id} request body
FieldTypeRequiredDescription
contact_emailstring or nullno
enabledboolean or nullnofalse revokes every key of the agent.
namestring or nullno
policy_idsarray of string or nullno
purposestring or nullno

Responses

PATCH /v1/agents/{agent_id} responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
404not_found: no such resource for this credential. A resource of another organization answers the same.
409The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

PATCH /v1/agents/{agent_id} response fields
FieldTypeRequiredDescription
budget_idstring or nullyes
budgetsarray of objectyes
contact_emailstring or nullyes
created_atstringyes
created_bystringyes
disabled_atstring or nullyes
enabledbooleanyes
idstringyes
keysarray of object ApiKeyyes
namestringyes
policy_idsarray of stringyes
project_idstring or nullyes
purposestring or nullyes
statusstringyes
updated_atstringyes

Example

curl https://api.faxforagents.com/v1/agents/AGENT_ID \
  -X PATCH \
  -H "Authorization: Bearer ffa_test_..." \
  -H "Content-Type: application/json" \
  -d '{}'

GET /v1/agents/{agent_id}/activity

The agent's audit trail

Credential: an API key or a signed-in session. Scope: agent:read.

Parameters

GET /v1/agents/{agent_id}/activity parameters
NameInTypeRequiredNotes
agent_idpathstringyes
cursorquerystring or nullno
limitqueryintegerno Default 50.

Responses

GET /v1/agents/{agent_id}/activity responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
404not_found: no such resource for this credential. A resource of another organization answers the same.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

GET /v1/agents/{agent_id}/activity response fields
FieldTypeRequiredDescription
itemsarray of object AgentActivityyes
next_cursorstring or nullno

Example

curl https://api.faxforagents.com/v1/agents/AGENT_ID/activity \
  -H "Authorization: Bearer ffa_test_..."

POST /v1/agents/{agent_id}/keys

Mint a key for the agent (bounded scopes, secret shown once)

Credential: an API key or a signed-in session. Scope: agent:admin.

Parameters

POST /v1/agents/{agent_id}/keys parameters
NameInTypeRequiredNotes
agent_idpathstringyes
Idempotency-KeyheaderstringnoReplay the first successful response for the same request within 24 hours. Reusing the idempotency header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay.

Request body application/json, required

POST /v1/agents/{agent_id}/keys request body
FieldTypeRequiredDescription
environmentstring (live, test)no
expires_in_daysinteger or nullno
labelstringno
scopesarray of string or nullnoOmit for the default agent set. Never approval:decide, key:admin, policy:admin, agent:admin.

Responses

POST /v1/agents/{agent_id}/keys responses
StatusMeaning
201Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
404not_found: no such resource for this credential. A resource of another organization answers the same.
409The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

POST /v1/agents/{agent_id}/keys response fields
FieldTypeRequiredDescription
agent_idstring or nullyes
connected_appbooleannoThe key belongs to a connected app used over stdio, such as the Gemini CLI extension. The organization's auto-send setting (Settings, Connected apps) binds it like an OAuth connection.
created_atstringyes
environmentstring (live, test)yes
expires_atstring or nullyes
idstringyes
labelstringyes
last_used_atstring or nullyes
prefixstringyes
project_idstring or nullyes
revoked_atstring or nullyes
rotated_from_idstring or nullyes
scopesarray of stringyes
secretstringyesShown once. Store it now; it cannot be read back.

Example

curl https://api.faxforagents.com/v1/agents/AGENT_ID/keys \
  -X POST \
  -H "Authorization: Bearer ffa_test_..." \
  -H "Content-Type: application/json" \
  -d '{}'