Hail Webhooks API
The webhooks API from Hail — 6 operation(s) for webhooks.
The webhooks API from Hail — 6 operation(s) for webhooks.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/hail-so:hail-so-webhooks-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Hail Webhooks API
description: 'Give your AI agent a voice, a real phone number, and an inbox.
This file is the source of truth for the Go CLI. Regenerate it after
changing API routes — see docs/public/contributing.md.'
version: 0.1.0
servers:
- url: https://api.hail.so
description: Hail Cloud
tags:
- name: Webhooks
paths:
/v1/webhooks:
post:
tags:
- Webhooks
summary: Create Subscription
description: 'Create a webhook subscription for one or more event types.
The response includes the plaintext signing secret — this is the only
time it is ever returned; store it now. Every later GET omits it. Use
POST /v1/webhooks/{sub_id}/rotate-secret to get a new plaintext secret if
it is lost or compromised.'
operationId: create_subscription_v1_webhooks_post
parameters:
- name: authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSubscriptionCreate'
responses:
'201':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSubscriptionResponse'
'429':
description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
RateLimit-Limit:
description: The request ceiling for the current window.
schema:
type: integer
RateLimit-Remaining:
description: Requests remaining in the current window.
schema:
type: integer
RateLimit-Reset:
description: Seconds until the current window resets.
schema:
type: integer
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
get:
tags:
- Webhooks
summary: List Subscriptions
description: 'List webhook subscriptions for the caller''s organization.
Cursor-paginated, newest first. The signing secret is never included —
only POST /v1/webhooks and POST /v1/webhooks/{sub_id}/rotate-secret return it.'
operationId: list_subscriptions_v1_webhooks_get
parameters:
- name: cursor
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Cursor
- name: limit
in: query
required: false
schema:
type: integer
maximum: 200
minimum: 1
default: 50
title: Limit
- name: authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSubscriptionListResponse'
'429':
description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
RateLimit-Limit:
description: The request ceiling for the current window.
schema:
type: integer
RateLimit-Remaining:
description: Requests remaining in the current window.
schema:
type: integer
RateLimit-Reset:
description: Seconds until the current window resets.
schema:
type: integer
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/v1/webhooks/event-types:
get:
tags:
- Webhooks
summary: List Event Types
description: 'List event names, labels, and channels accepted by subscriptions.
This public catalog contains no organization data. It uses the same
registry as create and patch validation.'
operationId: list_event_types_v1_webhooks_event_types_get
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookEventCatalogResponse'
'429':
description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
RateLimit-Limit:
description: The request ceiling for the current window.
schema:
type: integer
RateLimit-Remaining:
description: Requests remaining in the current window.
schema:
type: integer
RateLimit-Reset:
description: Seconds until the current window resets.
schema:
type: integer
/v1/webhooks/{sub_id}:
get:
tags:
- Webhooks
summary: Get Subscription
description: Fetch one webhook subscription by id. The signing secret is omitted.
operationId: get_subscription_v1_webhooks__sub_id__get
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
format: uuid
title: Sub Id
- name: authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSubscriptionResponse'
'429':
description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
RateLimit-Limit:
description: The request ceiling for the current window.
schema:
type: integer
RateLimit-Remaining:
description: Requests remaining in the current window.
schema:
type: integer
RateLimit-Reset:
description: Seconds until the current window resets.
schema:
type: integer
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
patch:
tags:
- Webhooks
summary: Patch Subscription
description: 'Update a webhook subscription''s target_url, event_types, and/or status.
Only fields present in the request body are changed. Setting
status="active" resets the consecutive-failure counter, so a
subscription that was auto-disabled after 50 straight delivery
failures does not immediately re-disable itself.'
operationId: patch_subscription_v1_webhooks__sub_id__patch
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
format: uuid
title: Sub Id
- name: authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSubscriptionPatch'
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSubscriptionResponse'
'429':
description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
RateLimit-Limit:
description: The request ceiling for the current window.
schema:
type: integer
RateLimit-Remaining:
description: Requests remaining in the current window.
schema:
type: integer
RateLimit-Reset:
description: Seconds until the current window resets.
schema:
type: integer
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
delete:
tags:
- Webhooks
summary: Delete Subscription
description: 'Permanently remove a webhook subscription.
Irreversible — no further events are delivered to it, and its
delivery history is deleted with it.'
operationId: delete_subscription_v1_webhooks__sub_id__delete
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
format: uuid
title: Sub Id
- name: authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
responses:
'204':
description: Successful Response
'429':
description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
RateLimit-Limit:
description: The request ceiling for the current window.
schema:
type: integer
RateLimit-Remaining:
description: Requests remaining in the current window.
schema:
type: integer
RateLimit-Reset:
description: Seconds until the current window resets.
schema:
type: integer
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/v1/webhooks/{sub_id}/rotate-secret:
post:
tags:
- Webhooks
summary: Rotate Secret
description: 'Generate a new signing secret and immediately invalidate the old one.
The response includes the new plaintext secret — this is the only
time it is returned; update your verification code with it right away,
since the old secret stops validating new deliveries immediately.'
operationId: rotate_secret_v1_webhooks__sub_id__rotate_secret_post
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
format: uuid
title: Sub Id
- name: authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSubscriptionResponse'
'429':
description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
RateLimit-Limit:
description: The request ceiling for the current window.
schema:
type: integer
RateLimit-Remaining:
description: Requests remaining in the current window.
schema:
type: integer
RateLimit-Reset:
description: Seconds until the current window resets.
schema:
type: integer
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/v1/webhooks/{sub_id}/deliveries:
get:
tags:
- Webhooks
summary: List Deliveries
description: 'List delivery attempts for one webhook subscription, newest first.
Cursor-paginated. Each entry shows the attempt count, response status,
and response body Hail recorded — use POST /v1/webhooks/{sub_id}/
deliveries/{delivery_id}/redeliver to retry a failed one.'
operationId: list_deliveries_v1_webhooks__sub_id__deliveries_get
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
format: uuid
title: Sub Id
- name: cursor
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Cursor
- name: limit
in: query
required: false
schema:
type: integer
maximum: 200
minimum: 1
default: 50
title: Limit
- name: authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookDeliveryListResponse'
'429':
description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
RateLimit-Limit:
description: The request ceiling for the current window.
schema:
type: integer
RateLimit-Remaining:
description: Requests remaining in the current window.
schema:
type: integer
RateLimit-Reset:
description: Seconds until the current window resets.
schema:
type: integer
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/v1/webhooks/{sub_id}/deliveries/{delivery_id}/redeliver:
post:
tags:
- Webhooks
summary: Redeliver
description: 'Retry one webhook delivery attempt.
Resets it to pending with a fresh attempt counter — the delivery
worker picks it up and re-sends the same event payload to target_url
shortly after. Useful after fixing an endpoint that was returning
errors.'
operationId: redeliver_v1_webhooks__sub_id__deliveries__delivery_id__redeliver_post
parameters:
- name: sub_id
in: path
required: true
schema:
type: string
format: uuid
title: Sub Id
- name: delivery_id
in: path
required: true
schema:
type: string
format: uuid
title: Delivery Id
- name: authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookDeliveryResponse'
'429':
description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
RateLimit-Limit:
description: The request ceiling for the current window.
schema:
type: integer
RateLimit-Remaining:
description: Requests remaining in the current window.
schema:
type: integer
RateLimit-Reset:
description: Seconds until the current window resets.
schema:
type: integer
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
components:
schemas:
WebhookDeliveryResponse:
properties:
id:
type: string
format: uuid
title: Id
description: Unique identifier for this delivery attempt.
subscription_id:
anyOf:
- type: string
format: uuid
- type: 'null'
title: Subscription Id
description: Subscription this delivery belongs to.
email_domain_id:
anyOf:
- type: string
format: uuid
- type: 'null'
title: Email Domain Id
description: Email domain the triggering event relates to, if any. Informational only (surfaced as the X-Hail-Email-Domain header) — not a routing target.
event_type:
type: string
title: Event Type
description: The event type being delivered (e.g. 'call.completed').
event_id:
type: string
format: uuid
title: Event Id
description: Identifier of the underlying event that triggered this delivery.
attempt:
type: integer
title: Attempt
description: Number of delivery attempts made so far for this event, starting at 0.
status:
type: string
enum:
- pending
- succeeded
- failed
- dead
title: Status
description: '''pending'' (queued/retrying), ''succeeded'', ''failed'' (will retry), or ''dead'' (retries exhausted).'
response_status:
anyOf:
- type: integer
- type: 'null'
title: Response Status
description: HTTP status code returned by the target URL on the last attempt. Null before any attempt.
response_body:
anyOf:
- type: string
- type: 'null'
title: Response Body
description: Response body returned by the target URL on the last attempt, if any. Null before any attempt.
next_attempt_at:
type: string
format: date-time
title: Next Attempt At
description: When the next delivery attempt is scheduled, ISO 8601 timestamp.
succeeded_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Succeeded At
description: When this delivery succeeded, ISO 8601 timestamp. Null until it does.
created_at:
type: string
format: date-time
title: Created At
description: When this delivery was queued, ISO 8601 timestamp.
type: object
required:
- id
- subscription_id
- email_domain_id
- event_type
- event_id
- attempt
- status
- next_attempt_at
- created_at
title: WebhookDeliveryResponse
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
type: array
title: Location
description: Path to the invalid field within the request, as a list of keys/indices (e.g. ['body', 'to']).
msg:
type: string
title: Message
description: Human-readable description of the validation failure.
type:
type: string
title: Error Type
description: Machine-readable error type code (e.g. 'missing', 'string_type').
input:
title: Input
description: The value that was actually provided and failed validation.
ctx:
type: object
title: Context
description: Additional machine-readable context for the error, when the error type provides one.
type: object
required:
- loc
- msg
- type
title: ValidationError
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
description: 'List of validation errors: each entry gives the field location (loc), the problem (msg), and the error type (type).'
type: object
title: HTTPValidationError
WebhookSubscriptionCreate:
properties:
target_url:
type: string
minLength: 1
title: Target Url
description: HTTPS URL Hail POSTs event payloads to.
event_types:
items:
$ref: '#/components/schemas/WebhookEventType'
type: array
minItems: 1
title: Event Types
description: Event types to subscribe to (e.g. 'call.completed', 'email.bounced'). At least one required.
type: object
required:
- target_url
- event_types
title: WebhookSubscriptionCreate
WebhookEventCatalogResponse:
properties:
items:
items:
$ref: '#/components/schemas/WebhookEventDefinition'
type: array
title: Items
description: Supported webhook events.
type: object
required:
- items
title: WebhookEventCatalogResponse
WebhookSubscriptionPatch:
properties:
target_url:
anyOf:
- type: string
- type: 'null'
title: Target Url
description: New delivery URL. Omit to leave unchanged.
event_types:
anyOf:
- items:
$ref: '#/components/schemas/WebhookEventType'
type: array
- type: 'null'
title: Event Types
description: New set of subscribed event types. Omit to leave unchanged.
status:
anyOf:
- type: string
enum:
- active
- disabled
- type: 'null'
title: Status
description: Set to 'disabled' to pause deliveries, or 'active' to resume. Omit to leave unchanged.
type: object
title: WebhookSubscriptionPatch
WebhookEventType:
type: string
enum:
- email.received
- email.delivered
- email.delivery_delayed
- email.bounced
- email.complained
- email.opened
- email.clicked
- email.received.suppressed
- email.send_failed
- sms.received
- sms.delivered
- sms.undelivered
- sms.failed
- call.received
- call.answered
- call.completed
- call.failed
- call.busy
- call.no_answer
- call.transferred
title: WebhookEventType
WebhookEventDefinition:
properties:
type:
$ref: '#/components/schemas/WebhookEventType'
description: Event identifier used in subscriptions.
label:
type: string
title: Label
description: Short display label for this event.
channel:
type: string
title: Channel
description: Display group, such as Email, Calls, or SMS.
type: object
required:
- type
- label
- channel
title: WebhookEventDefinition
WebhookSubscriptionListResponse:
properties:
items:
items:
$ref: '#/components/schemas/WebhookSubscriptionResponse'
type: array
title: Items
description: Subscriptions in this page.
next_cursor:
anyOf:
- type: string
- type: 'null'
title: Next Cursor
description: Opaque cursor for the next page. Null when there are no more results.
type: object
required:
- items
title: WebhookSubscriptionListResponse
WebhookSubscriptionResponse:
properties:
id:
type: string
format: uuid
title: Id
description: Unique identifier for this subscription.
organization_id:
type: string
format: uuid
title: Organization Id
description: Organization that owns this subscription.
target_url:
type: string
title: Target Url
description: HTTPS URL event payloads are POSTed to.
event_types:
items:
type: string
type: array
title: Event Types
description: Event types this subscription receives.
status:
type: string
enum:
- active
- disabled
title: Status
description: '''active'' (delivering) or ''disabled'' (paused).'
consecutive_failures:
type: integer
title: Consecutive Failures
description: Consecutive failed delivery attempts since the last success. Resets to 0 on success.
last_success_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Last Success At
description: When a delivery last succeeded, ISO 8601 timestamp. Null if never.
last_failure_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Last Failure At
description: When a delivery last failed, ISO 8601 timestamp. Null if never.
created_at:
type: string
format: date-time
title: Created At
description: When this subscription was created, ISO 8601 timestamp.
updated_at:
type: string
format: date-time
title: Updated At
description: When this subscription was last modified, ISO 8601 timestamp.
secret:
anyOf:
- type: string
- type: 'null'
title: Secret
description: Plaintext signing secret for verifying delivery payloads. Only present in the create and rotate-secret responses; every later read returns null.
type: object
required:
- id
- organization_id
- target_url
- event_types
- status
- consecutive_failures
- created_at
- updated_at
title: WebhookSubscriptionResponse
description: 'Subscription as returned by the API.
``secret`` is populated **only** by create + rotate-secret responses;
later GETs return ``None`` so the plaintext never round-trips.'
WebhookDeliveryListResponse:
properties:
items:
items:
$ref: '#/components/schemas/WebhookDeliveryResponse'
type: array
title: Items
description: Delivery attempts in this page.
next_cursor:
anyOf:
- type: string
- type: 'null'
title: Next Cursor
description: Opaque cursor for the next page. Null when there are no more results.
type: object
required:
- items
title: WebhookDeliveryListResponse