Register an agent
Create the agent's OAuth client, assign a human owner and an expiry date, issue a first token and verify the result in the inventory.
This walkthrough registers a complete agent identity: the OAuth client, its human owner, its expiry, a first token, and the inventory check. Read the agent identity model first if you have not.
Prerequisites
The examples use an admin API token (obx_ prefix) carrying the apps:manage permission, passed as Authorization: Bearer. Everything can also be done from the admin console (the Applications page has an AI agent option), where agent mutations additionally require a fresh MFA verification (step-up).
Every example uses accounts.obexal.com, the default domain. If your organization uses a custom domain, replace it accordingly.
Create the agent client
In the console, create an application and tick AI agent (Token Exchange delegation): the client type is forced to confidential and the machine grants are set automatically. Through the API, create the client explicitly:
curl -sS -X POST https://accounts.obexal.com/v1/applications \
-H "Authorization: Bearer $OBEXAL_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Support triage bot",
"clientType": "confidential",
"redirectUris": ["https://agents.example.eu/unused-callback"],
"scopes": ["openid", "tickets:read", "tickets:write"],
"grantTypes": ["client_credentials", "urn:ietf:params:oauth:grant-type:token-exchange"],
"requireConsent": true
}'{
"application": {
"clientId": "8Zl2vQx0T9hK3mW1s5nDgA",
"name": "Support triage bot",
"clientType": "confidential",
"grantTypes": ["client_credentials", "urn:ietf:params:oauth:grant-type:token-exchange"],
"scopes": ["openid", "tickets:read", "tickets:write"],
"tokenEndpointAuthMethod": "client_secret_basic",
"requireConsent": true
},
"clientSecret": "kq2Vt7...shown-only-once"
}Three fields deserve attention:
scopesis the agent's maximum perimeter: no token issued to this agent will ever exceed it. The list must includeopenid(client validation), so put the agent's real permissions next to it.grantTypes:client_credentialslets the agent act as itself, the token-exchange URN lets it act on behalf of a user. Include only what the agent needs.requireConsent: truemakes the agent governed: each user must explicitly authorize it before it can act on their behalf. Omit it only for trusted first-party agents.
Client validation requires at least one absolute http(s) redirectUris entry, even for a pure machine agent that never runs an interactive flow. Use a placeholder URL on a domain you own.
The clientSecret is returned once and never again (only its SHA-256 is stored). Put it in your secret manager immediately. If it is lost, rotate it.
Assign an owner and an expiry date
curl -sS -X PUT https://accounts.obexal.com/v1/admin/agents/8Zl2vQx0T9hK3mW1s5nDgA/identity \
-H "Authorization: Bearer $OBEXAL_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"owner": "alice@example.eu", "expiresAt": "2026-12-31T23:59:59Z"}'
# 204 No Contentowner must be the email of an existing user of your organization: it is stored as a directory reference, so the inventory can flag the agent as orphan if that person leaves. expiresAt is enforced fail-closed at issuance: past that date, every token request is refused. An empty string clears either field. A non-agent client or an unknown owner email returns 400 invalid_request; an unknown clientId returns 404.
Issue the first token
The agent authenticates with HTTP Basic on the token endpoint and uses the client_credentials grant:
curl -sS -X POST https://accounts.obexal.com/oauth/token \
-u "8Zl2vQx0T9hK3mW1s5nDgA:$AGENT_CLIENT_SECRET" \
-d grant_type=client_credentials \
-d scope="tickets:read"{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6IjIwMjYtMDYifQ...",
"token_type": "Bearer",
"expires_in": 600,
"scope": "tickets:read"
}The access token is a JWT (typ: at+jwt) with sub equal to the client_id: a machine identity, no user involved. No id_token and no refresh_token are issued for this grant. scope is optional and must be a subset of the client's scopes; the governance policy ceilings apply on top. This issuance also updates lastUsedAt.
Verify in the inventory
curl -sS https://accounts.obexal.com/v1/admin/agents \
-H "Authorization: Bearer $OBEXAL_API_TOKEN"{
"agents": [
{
"clientId": "8Zl2vQx0T9hK3mW1s5nDgA",
"name": "Support triage bot",
"ownerEmail": "alice@example.eu",
"expiresAt": "2026-12-31T23:59:59Z",
"lastUsedAt": "2026-07-02T09:14:07Z",
"status": "active",
"governed": true,
"enabled": true,
"needsReview": true,
"openAnomalies": 0
}
]
}needsReview stays true until a first attestation: POST /v1/admin/agents/8Zl2vQx0T9hK3mW1s5nDgA/review records it and returns 204.
Next steps
- Delegation with Token Exchange: have the agent act on behalf of a user.
- Per-agent governance policy: cap TTL, scopes and audiences before production.
- Secret rotation: give the secret an expiry and rotate it on schedule.