{"openapi":"3.0.3","info":{"title":"AHOOK","description":"Webhooks designed for agents: create a webhook URL, wait for events, list them, and extract fields. Free tier: 100 events stored, 7-day expiry, no signup.","version":"0.1.0"},"servers":[{"url":"https://saas.ahook.app"}],"paths":{"/api/v1/inboxes":{"post":{"summary":"Create a webhook inbox","description":"Returns inbox_id, the ingest URL, a read token (shown once), plan, and expiry. The slug in the URL is the public address part: the same slug is the inbox email address <slug>@ahook.app. The inbox_id + read token are private — they are what you use to read and manage the inbox.","responses":{"201":{"description":"Inbox created; read token shown once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Inbox"}}}}}}},"/api/v1/inboxes/{inbox_id}":{"get":{"summary":"Inbox details","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Inbox details."}}},"delete":{"summary":"Delete an inbox and its events","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted."}}}},"/api/v1/inboxes/{inbox_id}/events":{"get":{"summary":"List events","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"after","in":"query","schema":{"type":"integer"}},{"name":"limit","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"Events."}}}},"/api/v1/inboxes/{inbox_id}/wait":{"get":{"summary":"Wait for the next event after a cursor","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"after","in":"query","schema":{"type":"integer"}},{"name":"timeout","in":"query","schema":{"type":"integer"}},{"name":"body_contains","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"The next event, or {timeout: true} on expiry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}}}}},"/api/v1/inboxes/{inbox_id}/extract":{"post":{"summary":"Extract a JSON path from an event body","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Extracted value."}}}},"/ingest/{slug}":{"post":{"summary":"Public ingest endpoint for a webhook inbox","description":"Bodies are stored as text (1 MB cap). Binary bodies are stored base64-encoded with header x-ahook-encoding: base64 on Starter/Growth; free inboxes reject them with 403.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Event stored."}}}},"/api/v1/accounts":{"post":{"summary":"Create an account; API key shown once","responses":{"201":{"description":"Account created; API key shown once."}}}},"/api/v1/accounts/me":{"get":{"summary":"Account plan, status, and inbox usage (API key auth)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Account details."}}}},"/api/v1/accounts/checkout":{"post":{"summary":"Create a Stripe Checkout session for a price (API key auth)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Checkout URL."}}}},"/api/v1/accounts/cancel":{"post":{"summary":"Self-serve cancel at period end (API key auth)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Cancellation scheduled."}}}},"/api/v1/accounts/resume":{"post":{"summary":"Undo a scheduled cancel (API key auth)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Cancellation cleared."}}}},"/api/v1/accounts/inboxes":{"get":{"summary":"List the account’s inboxes (API key auth)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Inboxes."}}}},"/api/v1/accounts/custom-domains":{"post":{"summary":"Register a custom domain for an inbox (Growth, API key auth)","description":"Returns the TXT record to add (_ahook.<domain> = \"ahook-verify=<token>\") and the CNAME target. Verify with POST /verify. Optional \"slug\" sets a custom slug: email becomes <slug>@<domain> and https://<domain>/<slug> routes to the inbox.","security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Domain registered, pending verification."}}},"get":{"summary":"List the account’s custom domains (Growth, API key auth)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Custom domains and capacity usage."}}}},"/api/v1/accounts/custom-domains/verify":{"post":{"summary":"Verify a custom domain by its TXT record and provision it (Growth, API key auth)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Domain active."}}}},"/api/v1/accounts/custom-domains/{domain}":{"delete":{"summary":"Remove a custom domain (Growth, API key auth)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Domain removed."}}}},"/api/v1/internal/custom-domains":{"get":{"summary":"INTERNAL: active custom domains and their slugs (SMTP receiver allowlist)","description":"Bearer SMTP_RECEIVER_TOKEN. Returns every active custom domain with its slugs (custom slugs plus the linked inbox’s own slug). Used by the own SMTP receiver to reject unknown recipients at RCPT TO time. Not for public agents.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Active custom domains."},"401":{"description":"Bad token."},"503":{"description":"SMTP receiver not configured."}}}},"/api/v1/internal/email":{"post":{"summary":"INTERNAL: store one raw MIME message from the SMTP receiver","description":"Bearer SMTP_RECEIVER_TOKEN. Body {\"to\": \"<local>@<domain>\", \"raw\": \"<raw MIME text>\"}. The domain must be an active custom domain and the local part must resolve to an inbox (custom slug first, else the inbox’s own slug). Stored as an email.received event, same 1 MB cap behaviour as the Cloudflare email handler. Not for public agents.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Message stored."},"400":{"description":"Unknown domain or inbox."},"401":{"description":"Bad token."},"413":{"description":"Message too large."},"503":{"description":"SMTP receiver not configured."}}}},"/api/v1/stripe/webhook":{"post":{"summary":"Stripe webhook (signature-verified)","responses":{"200":{"description":"Event received."}}}},"/api/v1/health":{"get":{"summary":"Service status","responses":{"200":{"description":"OK."}}}},"/api/v1/inboxes/{inbox_id}/events/{event_id}":{"get":{"summary":"One event by id","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"event_id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"The event.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"401":{"description":"Unknown inbox or bad token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown event.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/inboxes/{inbox_id}/rotate-token":{"post":{"summary":"Replace the read token (shown once)","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"New read token.","content":{"application/json":{"schema":{"type":"object","properties":{"read_token":{"type":"string"}}}}}},"401":{"description":"Unknown inbox or bad token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/inboxes/{inbox_id}/subscriptions":{"post":{"summary":"Create a push subscription (paid-only, URL verified)","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Subscription created; signing secret shown once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"401":{"description":"Unknown inbox or bad token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Push subscriptions require Starter or Growth.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"summary":"List push subscriptions (no secrets)","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Subscriptions.","content":{"application/json":{"schema":{"type":"object","properties":{"subscriptions":{"type":"array","items":{"$ref":"#/components/schemas/Subscription"}}}}}}},"401":{"description":"Unknown inbox or bad token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/inboxes/{inbox_id}/subscriptions/{subscription_id}/verify":{"post":{"summary":"Retry URL verification for a subscription","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Verification result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"401":{"description":"Unknown inbox or bad token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown subscription.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/inboxes/{inbox_id}/subscriptions/{subscription_id}/replay":{"post":{"summary":"Replay one event to an active subscription","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Replay result.","content":{"application/json":{"schema":{"type":"object","properties":{"delivered":{"type":"boolean"},"response_code":{"type":"integer"}}}}}},"401":{"description":"Unknown inbox or bad token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown subscription or event.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Only active subscriptions receive replays.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/inboxes/{inbox_id}/subscriptions/{subscription_id}/deliveries":{"get":{"summary":"Delivery attempts for a subscription","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Delivery attempts."},"401":{"description":"Unknown inbox or bad token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown subscription.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/inboxes/{inbox_id}/subscriptions/{subscription_id}":{"delete":{"summary":"Delete a push subscription","security":[{"bearerAuth":[]}],"parameters":[{"name":"inbox_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted."},"401":{"description":"Unknown inbox or bad token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown subscription.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/accounts/rotate":{"post":{"summary":"Rotate the account API key (shown once)","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"New API key.","content":{"application/json":{"schema":{"type":"object","properties":{"account_id":{"type":"string"},"api_key":{"type":"string"}}}}}},"401":{"description":"Unknown account or bad API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1":{"get":{"summary":"Service index (all endpoints)","responses":{"200":{"description":"Service index."}}}},"/mcp":{"post":{"summary":"MCP (JSON-RPC 2.0, Streamable HTTP)","description":"Tools: create_inbox, wait_for_event, list_events, get_event, extract_field, delete_inbox. Inbox-scoped tools authenticate with Authorization: Bearer <read_token>.","responses":{"200":{"description":"JSON-RPC response."}}}}},"components":{"schemas":{"Inbox":{"type":"object","properties":{"inbox_id":{"type":"string"},"url":{"type":"string"},"read_token":{"type":"string","description":"Shown once at creation."},"plan":{"type":"string","enum":["free","starter","growth"]},"expires_at":{"type":"string","format":"date-time","nullable":true},"status":{"type":"string"}}},"Event":{"type":"object","properties":{"event_id":{"type":"integer"},"method":{"type":"string"},"path":{"type":"string"},"query":{"type":"string"},"headers_json":{"type":"string","description":"JSON object of headers, stored exactly as sent."},"body_text":{"type":"string"},"received_at":{"type":"integer"}}},"Account":{"type":"object","properties":{"account_id":{"type":"string"},"api_key":{"type":"string","description":"Shown once at creation."},"email":{"type":"string","nullable":true},"plan":{"type":"string"},"status":{"type":"string"},"inbox_count":{"type":"integer"},"inbox_limit":{"type":"integer"},"current_period_end":{"type":"string","format":"date-time","nullable":true}}},"Subscription":{"type":"object","properties":{"subscription_id":{"type":"string"},"url":{"type":"string"},"status":{"type":"string","enum":["active","pending","paused"]},"verified_at":{"type":"integer","nullable":true},"consecutive_failures":{"type":"integer"}}},"Error":{"type":"object","properties":{"error":{"type":"string","description":"Machine-readable code; see llms.txt Errors."},"message":{"type":"string"}}}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Read token returned once at inbox creation."}}}}