curl --request POST \
--url https://app.hookie.ai/v1/agents/register \
--header 'Content-Type: application/json' \
--data '
{
"agent_name": "<string>",
"accept_terms": "<string>",
"pow": {
"challenge": "<string>",
"nonce": "<string>"
},
"operator_contact": "<string>"
}
'const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
agent_name: '<string>',
accept_terms: '<string>',
pow: {challenge: '<string>', nonce: '<string>'},
operator_contact: '<string>'
})
};
fetch('https://app.hookie.ai/v1/agents/register', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.hookie.ai/v1/agents/register"
payload = {
"agent_name": "<string>",
"accept_terms": "<string>",
"pow": {
"challenge": "<string>",
"nonce": "<string>"
},
"operator_contact": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"account_id": "<string>",
"agent_name": "<string>",
"user_id": "<string>",
"workspace": {
"id": "<string>",
"name": "<string>",
"slug": "<string>",
"plan": "free"
},
"project": {
"id": "<string>",
"name": "<string>",
"slug": "<string>"
},
"api_key": {
"id": "<string>",
"key": "<string>",
"key_prefix": "<string>",
"role": "admin"
},
"claim": {
"token": "<string>",
"url": "<string>",
"note": "<string>"
},
"terms_version": "<string>",
"endpoints": {
"api": "<string>",
"mcp": "<string>",
"openapi": "<string>",
"docs": "<string>"
},
"notice": "<string>",
"usage": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true,
"workspace_id": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}Register an AI agent's own Hookie account
Public: no cookie, no key. Creates, all or nothing: the agent’s identity in WorkOS (a User Management user with a non-deliverable, verified address on agents.hookie.ai, external_id = the account id; no WorkOS organization), its Hookie user, a workspace on the Free plan with its Default project and the agent as owner, an admin API key, and a one-time claim token. The key and the claim token are in this response and nowhere else: Hookie stores only their hashes, never logs them, and never returns them again.
Controls, in order: a per-IP rate limit (an IPv6 address by its /64); the operator’s kill switch; body validation including accept_terms; the proof of work (signed, unexpired, solved; harder as the day’s cap fills); a platform-wide daily cap (default 200 a day, UTC) of which one network (an IPv4 address or an IPv6 /48) may take a tenth, both taken in the same statement that spends the challenge and before WorkOS is called. Free-plan quotas apply in full.
Idempotent. A challenge registers at most one account. Sending the same request again — the same challenge, or the same Idempotency-Key header with a new, unexpired and solved challenge — never creates a second account: it answers 409 already_registered with the account_id and NO credential (the credential is returned once; if it was lost, register again with a new challenge and a new Idempotency-Key). A key is scoped to the request: the same key with a different agent_name or operator_contact is a different registration. While the first request is still running the answer is 409 registration_in_progress; a request that died part-way is finished by a retry of the SAME request (its original challenge) after a minute, adopting the WorkOS user it may already have made — a key with a new challenge never finishes it. One never retried is abandoned after an hour: its WorkOS user is deleted and the retry gets challenge_spent. An attempt that failed (502) releases its key, so a new challenge with the same key registers.
What the key may do. Everything an admin can — projects, endpoints, rules, destinations, workflows, ingest keys, the rest — through /admin/api/*, the hosted MCP server at /mcp and the CLI. Billing, SSO and organization requests, invites, Google Workspace linking, platform administration, creating more API keys and revealing destination secrets answer 403 agent_not_permitted (with claim_required: true, except platform administration) until a person claims the workspace (and billing stays a person’s in the console after that). Audited as agent_registered.
curl --request POST \
--url https://app.hookie.ai/v1/agents/register \
--header 'Content-Type: application/json' \
--data '
{
"agent_name": "<string>",
"accept_terms": "<string>",
"pow": {
"challenge": "<string>",
"nonce": "<string>"
},
"operator_contact": "<string>"
}
'const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
agent_name: '<string>',
accept_terms: '<string>',
pow: {challenge: '<string>', nonce: '<string>'},
operator_contact: '<string>'
})
};
fetch('https://app.hookie.ai/v1/agents/register', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.hookie.ai/v1/agents/register"
payload = {
"agent_name": "<string>",
"accept_terms": "<string>",
"pow": {
"challenge": "<string>",
"nonce": "<string>"
},
"operator_contact": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"account_id": "<string>",
"agent_name": "<string>",
"user_id": "<string>",
"workspace": {
"id": "<string>",
"name": "<string>",
"slug": "<string>",
"plan": "free"
},
"project": {
"id": "<string>",
"name": "<string>",
"slug": "<string>"
},
"api_key": {
"id": "<string>",
"key": "<string>",
"key_prefix": "<string>",
"role": "admin"
},
"claim": {
"token": "<string>",
"url": "<string>",
"note": "<string>"
},
"terms_version": "<string>",
"endpoints": {
"api": "<string>",
"mcp": "<string>",
"openapi": "<string>",
"docs": "<string>"
},
"notice": "<string>",
"usage": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true,
"workspace_id": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"max_members": 123,
"members": 123,
"customer_id": "<string>",
"customer_name": "<string>",
"account_id": "<string>",
"terms_version": "<string>",
"claim_required": true
}Headers
Optional, 1–255 printable characters. A retry carrying the same key (with the same agent_name and operator_contact, and a live solved challenge) is answered 409 with the account it created, never a second account. A key whose attempt failed is released. Use a random value such as a UUID.
255Body
What the agent calls itself. Printable characters only. Names the workspace (<agent_name>'s workspace).
1 - 80Must equal the current Terms of Use version, 2026-10-01 (also in the challenge response as terms_version). By sending it the agent accepts the Terms at https://hookie.ai/legal/terms for the person or organization it acts for; the version is recorded on the account.
Show child attributes
Show child attributes
Optional: the email address or https URL of the person or organization responsible for the agent. Stored on the account and shown to the operator.
254Response
Registered. Store api_key.key and claim.token now: they are never shown again.
agt_…. The agent account.
The agent's WorkOS user id, also its Hookie user id.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
The agent's credential: an admin API key, shown here ONCE and stored only as its SHA-256. Send it as Authorization: Bearer <key> to /admin/api/* and /mcp, or set HOOKIE_TOKEN for the CLI. Listed under Settings → API keys as "Agent registration key".
Show child attributes
Show child attributes
A one-time token for the person responsible for the agent, shown here ONCE and stored only as its SHA-256. Signed in to Hookie, they open url (or POST the token to /admin/api/agent-accounts/claim) to become the workspace's owner — which they need to upgrade it.
Show child attributes
Show child attributes
Show child attributes
Show child attributes