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. 1. Register your agent below — you get a client_id instantly.

  2. 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=S256
  3. 3. 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. 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

One per line. Exact-match at authorize time.

Quickstart & endpoints

MCP (Streamable HTTP)

https://hekkapp.com/agents/mcp

REST API base

https://hekkapp.com/api/agent/v1

Authorize (consent)

https://hekkapp.com/en/agents/connect

Token

https://hekkapp.com/api/agent/v1/oauth/token

OAuth protected resource

https://hekkapp.com/.well-known/oauth-protected-resource

OAuth authorization server

https://hekkapp.com/.well-known/oauth-authorization-server

MCP discovery

https://hekkapp.com/.well-known/mcp

llms.txt

https://hekkapp.com/llms.txt

Scopes

discover

Discover & quote

Browse categories and providers, and request prices — read-only.

discover_servicesrequest_quoteget_bookinglist_bookings
message

Message providers

Send and read coordination messages with the assigned provider.

send_messageget_messages
book

Create & cancel bookings

Book services and cancel them within your caps.

create_bookingcancel_booking
complete

Confirm completion

Mark a job complete on your behalf.

confirm_completion
pay

Pay

Settle payment for a job within your caps.

pay
review

Leave reviews

Submit your own rating and words after a job (never fabricated).

submit_review

Tool reference

discover_services

scope: discover

Browse categories, services and ranked providers — read-only.

ParamTypeRequiredNotes
categorystringcategory slug; omit to list all categories
querystringsubstring match on provider name
minRatingnumber 0–5
maxStartingPriceBHD
limitint 1–50default 20
{ "categories": [{ "slug": "plumbing", "nameEn": "Plumbing", "nameAr": "السباكة" }] }

request_quote

scope: discover

Get the “starting from” price for a service from one or more providers.

ParamTypeRequiredNotes
serviceIdcuid
providerIdscuid[] 1–10omit = every provider offering the service
descriptionstring ≤2000
{ "quotes": [{ "providerId": "…", "providerName": "…", "startingPrice": 15.000, "currency": "BHD" }] }

create_booking

scope: bookIdempotency-Key required (REST)may return pending_human_approval

Create a booking. Cap-checked; 409 if the live price exceeds acceptedPrice; may pause for human approval.

ParamTypeRequiredNotes
providerIdcuid
serviceIdcuid
addressstring 5–300
latitudenumber
longitudenumber
scheduledAtISO date
notesstring ≤2000
acceptedPriceBHD409 if the live price exceeds this
{ "bookingId": "…", "status": "PENDING", "price": 25.000 }

get_booking

scope: discoverPII-gated

One booking's status and details. Provider phone is visible only after acceptance.

ParamTypeRequiredNotes
bookingIdcuid
{ "status": "ACCEPTED", "price": 25.000, "provider": { "name": "…", "phone": "+973…" } }

list_bookings

scope: discover

List the human's bookings, optionally filtered by status.

ParamTypeRequiredNotes
statusBookingStatus
limitint 1–100default 50
{ "bookings": [{ "bookingId": "…", "status": "COMPLETED", "hasReview": false }] }

send_message

scope: message

Send a coordination message to the assigned provider (marked via-agent).

ParamTypeRequiredNotes
bookingIdcuid
bodystring 1–2000
{ "messageId": "…", "at": "2026-01-01T10:00:00.000Z" }

get_messages

scope: message

Read the conversation with the assigned provider.

ParamTypeRequiredNotes
bookingIdcuid
limitint 1–200default 50
{ "messages": [{ "from": "PROVIDER", "body": "On my way", "viaAgent": false }] }

confirm_completion

scope: complete

Mark a job complete on the human's behalf.

ParamTypeRequiredNotes
bookingIdcuid
{ "bookingId": "…", "status": "COMPLETED" }

pay

scope: payIdempotency-Key required (REST)may return pending_human_approval

Settle payment within caps — authorize-only at launch, no online charge.

ParamTypeRequiredNotes
bookingIdcuid
amountBHD
{ "paymentId": "…", "status": "AUTHORIZED", "method": "CASH" }

submit_review

scope: reviewhumanAttested required

Submit the human's own rating + words, attributed via-agent. Never fabricated.

ParamTypeRequiredNotes
bookingIdcuid
ratingint 1–5
commentstring ≤2000
humanAttestedliteral trueattests these are the human's own words
{ "reviewId": "…", "attribution": "via_agent" }

cancel_booking

scope: book

Cancel a booking, applying the cancellation policy.

ParamTypeRequiredNotes
bookingIdcuid
reasonstring ≤500
{ "bookingId": "…", "status": "CANCELLED" }

get_audit_log

any grant

Read everything this agent has done for this human.

ParamTypeRequiredNotes
limitint 1–200default 50
{ "actions": [{ "tool": "create_booking", "result": "ok", "amount": 25.000 }] }

get_approval

any grant

Poll a pending human approval: APPROVED, DECLINED, EXPIRED, or still PENDING — including the linked bookingId once approved.

ParamTypeRequiredNotes
approvalIdstringfrom 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

codeHTTPmeaning
invalid_token401Missing or unrecognized access token.
token_expired401Access token expired — refresh it.
grant_revoked403The human revoked the grant (or it expired). Stop calling; re-request consent.
agent_suspended403The agent is suspended platform-wide.
insufficient_scope403The grant does not include the scope this tool needs.
cap_exceeded403Over a spend cap and not approvable inline.
pending_human_approval200Not 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_found404Unknown resource id for this grant.
conflict409State conflict — e.g. the live price now exceeds acceptedPrice.
disabled409This capability is switched off at platform level.
rate_limited429Over 120 requests/min for this grant — back off.
validation400Request body failed schema validation (details included).
internal500Unexpected 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.

← Back to the Agent Gateway overview