Build on Hekk
Register your agent, send your user through the Hekk consent screen, then call 12 tools over MCP or REST — the same registry powers both.
Agents are public OAuth clients: authorization-code + PKCE (S256). There is no client secret — the client_id is not a credential.
Quickstart
1. Register your agent below — you get a client_id instantly.
2. Send your user to the authorize URL (they log in and set scopes + caps):
https://hekkapp.com/en/agents/connect?response_type=code&client_id=<client_id>&redirect_uri=<redirect_uri>&scope=discover%20book&state=<state>&code_challenge=<S256_challenge>&code_challenge_method=S2563. Exchange the code for tokens (access 1h, refresh 30d rotating):
curl -X POST https://hekkapp.com/api/agent/v1/oauth/token \ -H "Content-Type: application/json" \ -d '{ "grant_type": "authorization_code", "code": "<code>", "redirect_uri": "<redirect_uri>", "client_id": "<client_id>", "code_verifier": "<pkce_verifier>" }'curl -X POST https://hekkapp.com/api/agent/v1/oauth/token \ -H "Content-Type: application/json" \ -d '{ "grant_type": "refresh_token", "refresh_token": "<refresh_token>", "client_id": "<client_id>" }'4. Call tools over MCP or REST — spending tools need an Idempotency-Key over REST:
{ "mcpServers": { "hekk": { "url": "https://hekkapp.com/agents/mcp" } } }curl -X POST https://hekkapp.com/api/agent/v1/tools/create_booking \ -H "Authorization: Bearer <access_token>" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: <unique-key>" \ -d '{ "providerId": "<providerId>", "serviceId": "<serviceId>", "address": "Building 12, Road 3419, Manama", "acceptedPrice": 25.000 }'
Access tokens live 1 hour, but the grant, agent and customer are re-validated on EVERY call — revocation and suspension bite immediately, not at expiry.
Register your agent
Quickstart & endpoints
MCP (Streamable HTTP)
https://hekkapp.com/agents/mcpREST API base
https://hekkapp.com/api/agent/v1Authorize (consent)
https://hekkapp.com/en/agents/connectToken
https://hekkapp.com/api/agent/v1/oauth/tokenOAuth protected resource
https://hekkapp.com/.well-known/oauth-protected-resourceOAuth authorization server
https://hekkapp.com/.well-known/oauth-authorization-serverMCP discovery
https://hekkapp.com/.well-known/mcpllms.txt
https://hekkapp.com/llms.txtScopes
discoverDiscover & quote
Browse categories and providers, and request prices — read-only.
discover_servicesrequest_quoteget_bookinglist_bookingsmessageMessage providers
Send and read coordination messages with the assigned provider.
send_messageget_messagesbookCreate & cancel bookings
Book services and cancel them within your caps.
create_bookingcancel_bookingcompleteConfirm completion
Mark a job complete on your behalf.
confirm_completionpayPay
Settle payment for a job within your caps.
payreviewLeave reviews
Submit your own rating and words after a job (never fabricated).
submit_reviewTool reference
discover_services
scope: discoverBrowse categories, services and ranked providers — read-only.
| Param | Type | Required | Notes |
|---|---|---|---|
| category | string | — | category slug; omit to list all categories |
| query | string | — | substring match on provider name |
| minRating | number 0–5 | — | |
| maxStartingPrice | BHD | — | |
| limit | int 1–50 | — | default 20 |
{ "categories": [{ "slug": "plumbing", "nameEn": "Plumbing", "nameAr": "السباكة" }] }request_quote
scope: discoverGet the “starting from” price for a service from one or more providers.
| Param | Type | Required | Notes |
|---|---|---|---|
| serviceId | cuid | ✓ | |
| providerIds | cuid[] 1–10 | — | omit = every provider offering the service |
| description | string ≤2000 | — |
{ "quotes": [{ "providerId": "…", "providerName": "…", "startingPrice": 15.000, "currency": "BHD" }] }create_booking
scope: bookIdempotency-Key required (REST)may return pending_human_approvalCreate a booking. Cap-checked; 409 if the live price exceeds acceptedPrice; may pause for human approval.
| Param | Type | Required | Notes |
|---|---|---|---|
| providerId | cuid | ✓ | |
| serviceId | cuid | ✓ | |
| address | string 5–300 | ✓ | |
| latitude | number | — | |
| longitude | number | — | |
| scheduledAt | ISO date | — | |
| notes | string ≤2000 | — | |
| acceptedPrice | BHD | ✓ | 409 if the live price exceeds this |
{ "bookingId": "…", "status": "PENDING", "price": 25.000 }get_booking
scope: discoverPII-gatedOne booking's status and details. Provider phone is visible only after acceptance.
| Param | Type | Required | Notes |
|---|---|---|---|
| bookingId | cuid | ✓ |
{ "status": "ACCEPTED", "price": 25.000, "provider": { "name": "…", "phone": "+973…" } }list_bookings
scope: discoverList the human's bookings, optionally filtered by status.
| Param | Type | Required | Notes |
|---|---|---|---|
| status | BookingStatus | — | |
| limit | int 1–100 | — | default 50 |
{ "bookings": [{ "bookingId": "…", "status": "COMPLETED", "hasReview": false }] }send_message
scope: messageSend a coordination message to the assigned provider (marked via-agent).
| Param | Type | Required | Notes |
|---|---|---|---|
| bookingId | cuid | ✓ | |
| body | string 1–2000 | ✓ |
{ "messageId": "…", "at": "2026-01-01T10:00:00.000Z" }get_messages
scope: messageRead the conversation with the assigned provider.
| Param | Type | Required | Notes |
|---|---|---|---|
| bookingId | cuid | ✓ | |
| limit | int 1–200 | — | default 50 |
{ "messages": [{ "from": "PROVIDER", "body": "On my way", "viaAgent": false }] }confirm_completion
scope: completeMark a job complete on the human's behalf.
| Param | Type | Required | Notes |
|---|---|---|---|
| bookingId | cuid | ✓ |
{ "bookingId": "…", "status": "COMPLETED" }pay
scope: payIdempotency-Key required (REST)may return pending_human_approvalSettle payment within caps — authorize-only at launch, no online charge.
| Param | Type | Required | Notes |
|---|---|---|---|
| bookingId | cuid | ✓ | |
| amount | BHD | ✓ |
{ "paymentId": "…", "status": "AUTHORIZED", "method": "CASH" }submit_review
scope: reviewhumanAttested requiredSubmit the human's own rating + words, attributed via-agent. Never fabricated.
| Param | Type | Required | Notes |
|---|---|---|---|
| bookingId | cuid | ✓ | |
| rating | int 1–5 | ✓ | |
| comment | string ≤2000 | — | |
| humanAttested | literal true | ✓ | attests these are the human's own words |
{ "reviewId": "…", "attribution": "via_agent" }cancel_booking
scope: bookCancel a booking, applying the cancellation policy.
| Param | Type | Required | Notes |
|---|---|---|---|
| bookingId | cuid | ✓ | |
| reason | string ≤500 | — |
{ "bookingId": "…", "status": "CANCELLED" }get_audit_log
any grantRead everything this agent has done for this human.
| Param | Type | Required | Notes |
|---|---|---|---|
| limit | int 1–200 | — | default 50 |
{ "actions": [{ "tool": "create_booking", "result": "ok", "amount": 25.000 }] }get_approval
any grantPoll a pending human approval: APPROVED, DECLINED, EXPIRED, or still PENDING — including the linked bookingId once approved.
| Param | Type | Required | Notes |
|---|---|---|---|
| approvalId | string | ✓ | from a pending_human_approval response |
{ "approvalId": "…", "status": "APPROVED", "tool": "create_booking", "bookingId": "…", "expiresAt": "…" }Guardrails, errors & limits
Spend gates
create_booking and pay pass three server-side gates: per-job cap, rolling-30-day cap (counting only this grant's non-dead via-agent bookings), and a first-booking-with-a-new-provider gate. A null cap means ALWAYS gated — not unlimited.
The pending_human_approval contract
A gated call succeeds with status pending_human_approval and a 30-minute confirm link. The human approves by email or in their Hekk account; on approval the platform replays your original request.
{
"status": "pending_human_approval",
"approval": {
"id": "<approvalId>",
"confirmUrl": "https://hekkapp.com/en/agents/approvals/<token>",
"expiresAt": "2026-01-01T12:30:00.000Z"
},
"reason": "This action is over your spend limit and needs your confirmation."
}
// Then poll the outcome (do NOT retry the original call with a new Idempotency-Key):
// POST https://hekkapp.com/api/agent/v1/tools/get_approval { "approvalId": "<approvalId>" }
// → { "status": "PENDING" | "APPROVED" | "DECLINED" | "EXPIRED", "bookingId": "…" }Always surface confirmUrl to your human — email delivery is best-effort.
Token lifetimes
- Access token — 1 hour (hga_…)
- Refresh token — 30 days, rotates on every use (hgr_…)
- Auth code — 5 minutes, single-use (hac_…)
- Approval confirm link — 30 minutes (apr_…)
Error codes
| code | HTTP | meaning |
|---|---|---|
| invalid_token | 401 | Missing or unrecognized access token. |
| token_expired | 401 | Access token expired — refresh it. |
| grant_revoked | 403 | The human revoked the grant (or it expired). Stop calling; re-request consent. |
| agent_suspended | 403 | The agent is suspended platform-wide. |
| insufficient_scope | 403 | The grant does not include the scope this tool needs. |
| cap_exceeded | 403 | Over a spend cap and not approvable inline. |
| pending_human_approval | 200 | Not an error — the action is parked for the human's confirm link (approvalId + confirmUrl included). Surface the link and wait; do not retry with a new Idempotency-Key. |
| not_found | 404 | Unknown resource id for this grant. |
| conflict | 409 | State conflict — e.g. the live price now exceeds acceptedPrice. |
| disabled | 409 | This capability is switched off at platform level. |
| rate_limited | 429 | Over 120 requests/min for this grant — back off. |
| validation | 400 | Request body failed schema validation (details included). |
| internal | 500 | Unexpected server error — safe to retry once, then report. |
Rate limits
120 requests/minute per grant over REST. Exceeding returns rate_limited.
Idempotency-Key and the rate limit apply to REST only today — prefer REST for spending-tool retries.
Change (Jul 2026): when an agent requests full access (no scope parameter), the consent screen now renders spending scopes (book, complete, pay, review) UNCHECKED by default — users must opt in explicitly. Narrow your scope request to exactly what your agent needs.