Agents
Agents are the AI systems that ask for approval. Each agent has instances, and each instance holds a credential it uses to call the API.
Tokens are returned once, when issued, and are never shown again.
Register an instance to get a credential directly, or create a provisioner token so each replica can create its own instance through the Agent Approval Protocol (AAP).
List agents
Returns the organization's agents. Each entry carries the agent's instance counts, when it was last seen, and how many requests it has made. Active agents come first, then disabled ones, then archived ones, each group sorted by name. Archived agents are left out unless status says otherwise.
An agent is live when one of its instances holds a credential that is neither expired nor revoked. last_seen_at is the newest authenticated call from any of its instances. Agents only call in when they have something to approve, so a quiet agent is not a broken one.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
status | query · enum | Which agents to return. One of live, archived, all |
limit | query · integer | How many agents to return. Defaults to 50. |
Response
Response codes
| Status | Body | Description |
|---|---|---|
200 | object | The agents |
400 | ErrorResponse |
|
403 | ErrorResponse | You cannot read agents |
Response body200
Example
curl -X GET "$WITHHUMAN_URL/api/v1/agents" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"{
"agents": [
{
"slug": "support-bot",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"name": "Support bot",
"kind": "service",
"status": "active",
"created_at": "2026-09-08T12:02:11Z",
"updated_at": "2026-09-08T12:02:11Z",
"archived_at": "2026-09-08T12:02:11Z",
"instance_count": 0,
"live_instance_count": 0,
"last_seen_at": "2026-09-08T12:02:11Z",
"recent_request_count": 0,
"pending_request_count": 0,
"request_trend": [
0
]
}
]
}Create an agent
Creates an agent. An agent is a stable identity for one AI system, such as a support bot or a deploy assistant. Its slug is the identity: chosen here, never changed, never reused. Agents own no pipeline of their own: every request starts in the organization's entry pipeline, and rules for one agent are a branch or a delegate on agent.slug there.
Creating an agent does not issue a credential. Register an instance, or create a provisioner token, to let it call the API.
Request
Request body
The agent's identity. Lowercase letters, digits, dots, underscores and hyphens, up to 63 characters. It cannot change and is never reused, so a slug an archived agent holds is refused.
A display name. Reviewers see it on every request the agent makes. It can be changed later and must be unique among the organization's live agents.
Whose behalf the agent's instances act on. service for an agent the organization runs, personal for one people run for themselves; see the Agent schema. It cannot be changed later.
One of service, personal
Response
Response codes
| Status | Body | Description |
|---|---|---|
201 | Agent | The new agent |
403 | ErrorResponse | You cannot create agents |
409 | ErrorResponse | The slug is taken by a live or archived agent ( |
429 | The organization is at its agent limit |
Response body201
The agent's identity, chosen once at creation. It never changes and is never reused, not even after the agent is archived.
The membership or organization-key actor that created the agent.
The display name. Reviewers see it on every request. Editable, and unique among the organization's live agents.
Whose behalf the agent's instances act on, chosen once. A service agent is one the organization runs, such as a support bot: its instances share one identity, administrators enroll them, and provisioner tokens mint them. A personal agent is one people run for themselves, such as a coding assistant: every instance belongs to the member who enrolled it, any active member may connect their own, and the instances are disabled when that member is removed. Personal agents have no provisioner tokens.
One of service, personal
A disabled agent's instances cannot authenticate until it is enabled again. An archived agent is retired: nothing of it authenticates, nothing can be enrolled under it, and its status cannot change until it is restored.
One of active, disabled, archived
When the agent was archived. Present exactly when status is archived.
Example
curl -X POST "$WITHHUMAN_URL/api/v1/agents" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"slug": "support-bot",
"name": "Support bot",
"kind": "service"
}'{
"slug": "support-bot",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"name": "Support bot",
"kind": "service",
"status": "active",
"created_at": "2026-09-08T12:02:11Z",
"updated_at": "2026-09-08T12:02:11Z",
"archived_at": "2026-09-08T12:02:11Z"
}Retrieve an agent
Returns one agent with a page of instances, each instance's credentials, and its provisioner tokens. Secrets are never included. Instances are sorted by last seen, then creation time and id, newest first. Counts and connection state cover all instances, independent of the page and filters.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | path · string | The agent's slug: the identity chosen when it was created, never changed and never reused. |
instance_limit | query · integer | Number of instances in this page. |
instance_offset | query · integer | Number of matching instances to skip. |
instance_q | query · string | Case-insensitive literal substring of the instance name. |
instance_status | query · enum | Filter by whether an instance is enabled or disabled, independently of its credentials. One of active, disabled |
provisioner_limit | query · integer | Number of provisioner tokens in this page, sorted by issued time and id, newest first. |
provisioner_offset | query · integer | Number of matching provisioner tokens to skip. |
provisioner_q | query · string | Case-insensitive literal substring of the token identifier or displayed identifier suffix. Searches identifiers, never secrets. |
provisioner_status | query · enum | Token state, with revocation taking precedence over expiry. One of active, expired, revoked |
Response
Response codes
| Status | Body | Description |
|---|---|---|
200 | Agent | The agent |
400 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
403 | ErrorResponse | You cannot read agents |
404 | ErrorResponse | No such agent |
Response body200
The agent's identity, chosen once at creation. It never changes and is never reused, not even after the agent is archived.
The membership or organization-key actor that created the agent.
The display name. Reviewers see it on every request. Editable, and unique among the organization's live agents.
Whose behalf the agent's instances act on, chosen once. A service agent is one the organization runs, such as a support bot: its instances share one identity, administrators enroll them, and provisioner tokens mint them. A personal agent is one people run for themselves, such as a coding assistant: every instance belongs to the member who enrolled it, any active member may connect their own, and the instances are disabled when that member is removed. Personal agents have no provisioner tokens.
One of service, personal
A disabled agent's instances cannot authenticate until it is enabled again. An archived agent is retired: nothing of it authenticates, nothing can be enrolled under it, and its status cannot change until it is restored.
One of active, disabled, archived
When the agent was archived. Present exactly when status is archived.
All provisioner tokens owned by this agent.
Tokens matching the current identifier and status filters before pagination.
All instances owned by this agent.
Active instances with at least one live credential across all pages.
Instances matching the current search and status filters before pagination.
All requests made by this agent.
One of human, api_key
The name of the human or organization key that created the agent.
The requested page of instances, sorted by last seen, creation time, and id, newest first.
The requested page of provisioner tokens, sorted by issued time and id, newest first. Secrets are never included.
Requests made in the last seven days.
Requests still waiting for a decision.
Example
curl -X GET "$WITHHUMAN_URL/api/v1/agents/{slug}" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"{
"slug": "support-bot",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"name": "Support bot",
"kind": "service",
"status": "active",
"created_at": "2026-09-08T12:02:11Z",
"updated_at": "2026-09-08T12:02:11Z",
"archived_at": "2026-09-08T12:02:11Z",
"provisioner_count": 0,
"matching_provisioner_count": 0,
"instance_count": 0,
"live_instance_count": 0,
"matching_instance_count": 0,
"request_count": 0,
"created_by_actor_type": "human",
"created_by_display_name": "string",
"instances": [
{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"agent_slug": "string",
"membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"name": "ci-runner-07",
"status": "active",
"metadata": {
"region": "eu-west"
},
"created_at": "2026-09-08T12:02:11Z",
"last_seen_at": "2026-09-08T12:02:11Z",
"delivery": {
"type": "webhook",
"url": "https://harness.example.com/aap/events",
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
},
"owner_display_name": "string",
"credentials": [
{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"issued_at": "2026-09-08T12:02:11Z",
"expires_at": "2026-09-08T12:02:11Z",
"last_used_at": "2026-09-08T12:02:11Z",
"revoked_at": "2026-09-08T12:02:11Z"
}
],
"recent_request_count": 0,
"request_trend": [
0
]
}
],
"provisioners": [
{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"agent_slug": "string",
"created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"issued_at": "2026-09-08T12:02:11Z",
"expires_at": "2026-09-08T12:02:11Z",
"last_used_at": "2026-09-08T12:02:11Z",
"revoked_at": "2026-09-08T12:02:11Z"
}
],
"recent_request_count": 0,
"pending_request_count": 0
}Update an agent
Changes an agent's status or its display name, the two fields that can change after creation. The slug never changes. Who manages an agent is decided by the role assignments at the agent, not by a property of it.
Disabling an agent keeps its instances and credentials in place, but none of them can authenticate until the agent is enabled again. An archived agent is past both switches; restore it first.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | path · string | The agent's slug: the identity chosen when it was created, never changed and never reused. |
Request body
A new display name, unique among the organization's live agents. Audited as agent.renamed.
Response
Response codes
| Status | Body | Description |
|---|---|---|
200 | Agent | The updated agent |
400 | ErrorResponse | Neither status nor name was given, or the status is invalid |
403 | ErrorResponse | You cannot change agents |
404 | ErrorResponse | No such agent |
409 | ErrorResponse | The agent is archived ( |
Response body200
The agent's identity, chosen once at creation. It never changes and is never reused, not even after the agent is archived.
The membership or organization-key actor that created the agent.
The display name. Reviewers see it on every request. Editable, and unique among the organization's live agents.
Whose behalf the agent's instances act on, chosen once. A service agent is one the organization runs, such as a support bot: its instances share one identity, administrators enroll them, and provisioner tokens mint them. A personal agent is one people run for themselves, such as a coding assistant: every instance belongs to the member who enrolled it, any active member may connect their own, and the instances are disabled when that member is removed. Personal agents have no provisioner tokens.
One of service, personal
A disabled agent's instances cannot authenticate until it is enabled again. An archived agent is retired: nothing of it authenticates, nothing can be enrolled under it, and its status cannot change until it is restored.
One of active, disabled, archived
When the agent was archived. Present exactly when status is archived.
Example
curl -X PATCH "$WITHHUMAN_URL/api/v1/agents/{slug}" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "active",
"name": "Support bot"
}'{
"slug": "support-bot",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"name": "Support bot",
"kind": "service",
"status": "active",
"created_at": "2026-09-08T12:02:11Z",
"updated_at": "2026-09-08T12:02:11Z",
"archived_at": "2026-09-08T12:02:11Z"
}Archive an agent
Archives the agent. Every live credential and provisioner token is revoked. Nothing is deleted: the agent is still listed with status=archived or all, it can still be retrieved with its instances and revoked credentials, and its requests and audit trail stay readable. But no instance or provisioner token can be created for it and its status cannot change. Its name is freed for a new agent; its slug is not, and never will be. Pipelines are the organization's, not the agent's, and are untouched.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | path · string | The agent's slug: the identity chosen when it was created, never changed and never reused. |
Response
Response codes
| Status | Body | Description |
|---|---|---|
204 | The agent is archived | |
401 | ErrorResponse | You are not signed in |
403 | ErrorResponse | You cannot change agents |
404 | ErrorResponse | No such agent |
409 | ErrorResponse | The agent is already archived ( |
Example
curl -X DELETE "$WITHHUMAN_URL/api/v1/agents/{slug}" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"Restore an archived agent
Brings an archived agent back to active. Credentials and provisioner tokens revoked when the agent was archived stay revoked; register instances or connect them again to let it call the API. The name must still be free among the organization's live agents.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | path · string | The agent's slug: the identity chosen when it was created, never changed and never reused. |
Response
Response codes
| Status | Body | Description |
|---|---|---|
200 | Agent | The restored agent |
401 | ErrorResponse | You are not signed in |
403 | ErrorResponse | You cannot change agents |
404 | ErrorResponse | No such agent |
409 | ErrorResponse | The agent is not archived ( |
Response body200
The agent's identity, chosen once at creation. It never changes and is never reused, not even after the agent is archived.
The membership or organization-key actor that created the agent.
The display name. Reviewers see it on every request. Editable, and unique among the organization's live agents.
Whose behalf the agent's instances act on, chosen once. A service agent is one the organization runs, such as a support bot: its instances share one identity, administrators enroll them, and provisioner tokens mint them. A personal agent is one people run for themselves, such as a coding assistant: every instance belongs to the member who enrolled it, any active member may connect their own, and the instances are disabled when that member is removed. Personal agents have no provisioner tokens.
One of service, personal
A disabled agent's instances cannot authenticate until it is enabled again. An archived agent is retired: nothing of it authenticates, nothing can be enrolled under it, and its status cannot change until it is restored.
One of active, disabled, archived
When the agent was archived. Present exactly when status is archived.
Example
curl -X POST "$WITHHUMAN_URL/api/v1/agents/{slug}/restore" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"{
"slug": "support-bot",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"name": "Support bot",
"kind": "service",
"status": "active",
"created_at": "2026-09-08T12:02:11Z",
"updated_at": "2026-09-08T12:02:11Z",
"archived_at": "2026-09-08T12:02:11Z"
}Update an instance
Changes an instance's status or its display name, the two fields that can change after enrollment. The agent and its other instances are not affected.
Disabling an instance keeps its credentials, but none of them can authenticate until it is enabled again. Renaming changes what the instance is called from now on: nothing addresses an instance by name after enrollment, so the running agent needs no change, and requests already made keep the name the instance had at the time.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
idrequired | path · uuid | The instance's id. |
Request body
A new display name, unique within the agent. Audited as agent_instance.renamed.
Response
Response codes
| Status | Body | Description |
|---|---|---|
200 | AgentInstance | The updated instance |
400 | ErrorResponse | Neither status nor name was given, or the status is invalid |
403 | ErrorResponse | You cannot change agents |
404 | ErrorResponse | No such instance |
409 | ErrorResponse | The instance's agent is archived ( |
Response body200
The slug of the agent this is an instance of.
The member this instance belongs to and acts for. Present exactly when the agent is personal.
Unique within the agent.
The JSON stored when the instance was created.
The last time this instance called the API.
The verified receiver of an instance using asynchronous mode, returned when the instance is created. The signing secret is never returned.
Example
curl -X PATCH "$WITHHUMAN_URL/api/v1/agent_instances/{id}" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "active",
"name": "support-server-01"
}'{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"agent_slug": "string",
"membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"name": "ci-runner-07",
"status": "active",
"metadata": {
"region": "eu-west"
},
"created_at": "2026-09-08T12:02:11Z",
"last_seen_at": "2026-09-08T12:02:11Z",
"delivery": {
"type": "webhook",
"url": "https://harness.example.com/aap/events",
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
}
}Register an instance
Registers an instance of an agent and issues its credential. An instance is one running copy of the agent, such as a CI runner or a pod. The credential's token is returned once, in this response. Store it where the instance can read it. It cannot be retrieved again.
Use this when you set up an instance by hand. For a fleet that starts replicas itself, create a provisioner token instead and let each replica create its own instance through the Agent Approval Protocol (AAP).
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | path · string | The agent's slug: the identity chosen when it was created, never changed and never reused. |
Request body
A name for the instance, unique within the agent. Reviewers see it next to the agent's name.
Any JSON object to store with the instance, such as a region or pod name.
The receiver for a registration you started. It is verified before the instance is created.
Response
Response codes
| Status | Body | Description |
|---|---|---|
201 | AgentInstanceCredential | The new instance and its credential. The token appears only in this response. |
400 | ErrorResponse | An organization key tried to register an instance of a personal agent, which belongs to a person ( |
403 | ErrorResponse | You cannot issue credentials |
404 | ErrorResponse | No such agent, or no registration you started for it |
409 | ErrorResponse | The agent is archived ( |
410 | ErrorResponse | The registration expired or was used ( |
422 | ErrorResponse | The receiver did not answer the verification ( |
501 | ErrorResponse | This server has no secrets key, so it cannot deliver webhooks ( |
Response body201
One running copy of an agent.
Example
curl -X POST "$WITHHUMAN_URL/api/v1/agents/{slug}/instances" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "ci-runner-07",
"metadata": {
"region": "eu-west"
},
"delivery": {
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"url": "https://harness.example.com/aap/events"
}
}'{
"agent_instance": {
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"agent_slug": "string",
"membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"name": "ci-runner-07",
"status": "active",
"metadata": {
"region": "eu-west"
},
"created_at": "2026-09-08T12:02:11Z",
"last_seen_at": "2026-09-08T12:02:11Z",
"delivery": {
"type": "webhook",
"url": "https://harness.example.com/aap/events",
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
}
},
"credential": {
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"token": "whc_live_3f6b9c1d0e7a4b2c",
"expires_at": "2026-09-08T12:02:11Z"
}
}Start a webhook registration
Starts registering an instance that is notified when its requests are decided, so the agent can pause instead of polling. The response holds a registration ID and a signing secret. The secret appears only in this response.
Configure your receiver with both, then register the instance with delivery naming this registration and the receiver URL. The receiver must answer a signed webhook.verification challenge before the instance is created. The registration expires after one hour.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | path · string | The agent's slug: the identity chosen when it was created, never changed and never reused. |
Response
Response codes
| Status | Body | Description |
|---|---|---|
201 | AgentInstanceRegistration | The registration. The signing secret appears only in this response. |
400 | ErrorResponse | An organization key tried to register an instance of a personal agent, which belongs to a person ( |
403 | ErrorResponse | You cannot issue credentials |
404 | ErrorResponse | No such agent |
409 | ErrorResponse | The agent is archived ( |
501 | ErrorResponse | This server has no secrets key, so it cannot deliver webhooks ( |
Response body201
The ID the verification message names. Your receiver uses it to choose the signing secret.
The receiver's signing secret: whsec_ followed by 32 random bytes in padded base64. It is never returned again.
When the registration can no longer be completed.
Example
curl -X POST "$WITHHUMAN_URL/api/v1/agents/{slug}/instance_registrations" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"{
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"signing_secret": "string",
"expires_at": "2026-09-08T12:02:11Z"
}Create a provisioner token
Creates a provisioner token for an agent. A provisioner token lets a fleet or platform create instances of this agent on its own, each with a credential of its own, through the Agent Approval Protocol (AAP). It cannot create approval requests itself.
The token is returned once, in this response. It cannot be retrieved again.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | path · string | The agent's slug: the identity chosen when it was created, never changed and never reused. |
Response
Response codes
| Status | Body | Description |
|---|---|---|
201 | AgentProvisionerCredential | The provisioner and its token. The token appears only in this response. |
400 | ErrorResponse | The agent is personal, and personal agents have no provisioner tokens ( |
403 | ErrorResponse | You cannot issue credentials |
404 | ErrorResponse | No such agent |
409 | ErrorResponse | The agent is archived ( |
Response body201
A provisioner token as the agent page shows it. The token is never included.
Example
curl -X POST "$WITHHUMAN_URL/api/v1/agents/{slug}/provisioners" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"{
"provisioner": {
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"agent_slug": "string",
"created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"issued_at": "2026-09-08T12:02:11Z",
"expires_at": "2026-09-08T12:02:11Z",
"last_used_at": "2026-09-08T12:02:11Z",
"revoked_at": "2026-09-08T12:02:11Z"
},
"credential": {
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"token": "whc_live_3f6b9c1d0e7a4b2c",
"expires_at": "2026-09-08T12:02:11Z"
}
}Revoke a provisioner token
Revokes a provisioner token. It stops working immediately. The instances it already created, and their credentials, are not affected.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
idrequired | path · uuid | The provisioner's id. |
Response
Response codes
| Status | Body | Description |
|---|---|---|
204 | The token is revoked | |
403 | ErrorResponse | You cannot revoke credentials |
404 | ErrorResponse | No such provisioner |
Example
curl -X DELETE "$WITHHUMAN_URL/api/v1/agent-provisioners/{id}" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"Revoke a credential
Revokes one instance credential. It stops working immediately. The instance stays registered, and the agent and its other instances are not affected.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
idrequired | path · uuid | The credential's id. |
Response
Response codes
| Status | Body | Description |
|---|---|---|
204 | The credential is revoked | |
403 | ErrorResponse | You cannot revoke credentials |
404 | ErrorResponse | No such credential |
Example
curl -X DELETE "$WITHHUMAN_URL/api/v1/agent-credentials/{id}" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"Report the calling instance's tool catalogue
/api/v1/agent_instances/self/tool_catalogBearer tokenReplaces complete source snapshots in one discovery context. Failed sources retain their last successful definitions. The instance credential supplies organization and instance identity. No arguments or credentials belong in a report. This product endpoint is outside AAP. Maximum body size is 8 MiB.
Request
Request body
Stable opaque identifier for this local project or runtime context
Whether all configured sources in this context were enumerated
Response
Response codes
| Status | Body | Description |
|---|---|---|
204 | Catalogue report recorded | |
400 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
401 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
403 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
Example
curl -X PUT "$WITHHUMAN_URL/api/v1/agent_instances/self/tool_catalog" \
-H "Authorization: Bearer $WITHHUMAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"runtime": "string",
"discovery_context": "string",
"complete": true,
"sources": [
{
"source": "builtin",
"server": "string",
"complete": true,
"error": "string",
"tools": [
{
"name": "string",
"display_name": "string",
"description": "string",
"input_schema": {},
"annotations": {
"title": "string",
"read_only_hint": true,
"destructive_hint": true,
"idempotent_hint": true,
"open_world_hint": true
}
}
]
}
]
}'List known tool definitions
Returns definitions within the caller's agent scope. An edition's own tool source (the hosted gateway) is refreshed on read through a five-minute shared cache, with that edition's live grants applied. Failed refreshes retain the last successful definitions and mark their source stale. Catalogue information is advisory and never changes approval decisions.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
agent_slug | query · string | |
q | query · string | |
source | query · string | Only tools from this source: |
status | query · enum | One of available, unavailable, stale |
limit | query · integer | |
offset | query · integer | |
refresh | query · boolean | Bypass the edition's tool source discovery cache. Local tools require a CLI report. |
Response
Response codes
| Status | Body | Description |
|---|---|---|
200 | ToolCatalogPage | Known definitions and source discovery status |
400 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
401 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
403 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
Response body200
Example
curl -X GET "$WITHHUMAN_URL/api/v1/tool_catalog" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"{
"tools": [
{
"name": "string",
"display_name": "string",
"description": "string",
"input_schema": {},
"annotations": {
"title": "string",
"read_only_hint": true,
"destructive_hint": true,
"idempotent_hint": true,
"open_world_hint": true
},
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"agent_slug": "string",
"agent_instance_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"instance_name": "string",
"discovery_context": "string",
"runtime": "string",
"source": "string",
"server": "string",
"available": true,
"stale": true,
"error": "string",
"schema_fingerprint": "string",
"last_seen_at": "2026-09-08T12:02:11Z",
"last_attempt_at": "2026-09-08T12:02:11Z"
}
],
"sources": [
{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"agent_slug": "string",
"agent_instance_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"instance_name": "string",
"available": true,
"runtime": "string",
"discovery_context": "string",
"source": "string",
"server": "string",
"last_attempt_at": "2026-09-08T12:02:11Z",
"last_success_at": "2026-09-08T12:02:11Z",
"error": "string"
}
],
"total": 0,
"next_offset": 1
}Read a tool definition
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
idrequired | path · uuid | |
agent_slug | query · string |
Response
Response codes
| Status | Body | Description |
|---|---|---|
200 | ToolCatalogEntryDefinition | Definition and its discovery provenance |
401 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
403 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
404 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
Response body200
builtin, mcp, or a source an edition adds (the hosted gateway's gateway)
Example
curl -X GET "$WITHHUMAN_URL/api/v1/tool_catalog/{id}" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"{
"name": "string",
"display_name": "string",
"description": "string",
"input_schema": {},
"annotations": {
"title": "string",
"read_only_hint": true,
"destructive_hint": true,
"idempotent_hint": true,
"open_world_hint": true
},
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"agent_slug": "string",
"agent_instance_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"instance_name": "string",
"discovery_context": "string",
"runtime": "string",
"source": "string",
"server": "string",
"available": true,
"stale": true,
"error": "string",
"schema_fingerprint": "string",
"last_seen_at": "2026-09-08T12:02:11Z",
"last_attempt_at": "2026-09-08T12:02:11Z"
}List distinct tool definitions
Returns the catalogue within the caller's agent scope in one response, folded to distinct definitions: a tool name on a server with one schema and description appears once, listing every runtime and instance that offers it, however many agents see it. Also returns every server a catalogue source names, including sources where discovery found no tools. The edition's own tool source is refreshed as for the catalogue listing. Useful for suggesting tools and argument fields while writing pipeline conditions; the information is advisory and never changes approval decisions.
Request
No parameters or body.
Response
Response codes
| Status | Body | Description |
|---|---|---|
200 | ToolDefinitionList | Distinct definitions and the servers the catalogue names |
401 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
403 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
Response body200
Example
curl -X GET "$WITHHUMAN_URL/api/v1/tool_definitions" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"{
"tool_definitions": [
{
"name": "string",
"server": "string",
"description": "string",
"input_schema": {},
"uses": [
{
"runtime": "string",
"instance_name": "string",
"available": true,
"stale": true
}
]
}
],
"servers": [
"string"
]
}Read an agent's gateway tool accessHosted edition
Requires gateway.read and agent.read for the named agent. Unconfigured servers grant no access. All instances inherit these grants.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | path · string |
Response
Response codes
| Status | Body | Description |
|---|---|---|
200 | object | Saved grants, including no-access grants retained for revision checks |
401 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
403 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
404 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
501 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
Response body200
Example
curl -X GET "$WITHHUMAN_URL/api/v1/agents/{slug}/tool-access" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY"{
"grants": [
{
"server_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"server_slug": "string",
"mode": "none",
"tools": [
"string"
],
"scopes": [
"string"
],
"revision": 1
}
]
}Replace an agent's access to a server's toolsHosted edition
Requires gateway.write and agent.read for the named agent. Grants exact tool names on this server, all current and future tools, or no tools, and the scopes the agent may call them with: any granted scope works with any granted tool. Every scope must be one of the server's current scopes. A member-held scope can be granted to a personal agent only (400 otherwise); its instances call with each owner's own connection. Supply the current revision, or zero for an unconfigured pair. A stale revision returns 412; an identical retry of the last write returns its committed result without another audit event. This version is the retry identity, so no Idempotency-Key is required. Archived agents and servers refuse edits. Inactive servers can be configured. The next MCP tool listing reflects changes; calls check live access before execution, including after approval. Already dispatched calls cannot be undone.
Request
Parameters
| Parameter | Type | Description |
|---|---|---|
slugrequired | path · string | |
server_slugrequired | path · string |
Request body
One of none, selected, all
Exact downstream tool names, meaningful only in selected mode. Empty for none or all; no wildcard matching.
The scopes the agent may call with; any granted scope works with any granted tool. Empty exactly when mode is none.
Expected current version; zero for an unconfigured agent/server pair.
Response
Response codes
| Status | Body | Description |
|---|---|---|
200 | AgentToolAccess | Saved grant with its new revision |
400 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
401 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
403 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
404 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
409 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
412 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
501 | ErrorResponse | Error response. A 403 from a permission check carries ForbiddenDetails in error.details. |
Response body200
One of none, selected, all
Example
curl -X PUT "$WITHHUMAN_URL/api/v1/agents/{slug}/tool-access/{server_slug}" \
-H "Authorization: Bearer $WITHHUMAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "none",
"tools": [
"string"
],
"scopes": [
"string"
],
"revision": 0
}'{
"server_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"server_slug": "string",
"mode": "none",
"tools": [
"string"
],
"scopes": [
"string"
],
"revision": 1
}