Work with this as data
Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
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.
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/hail-so:hail-so-sms-api"
All apis
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.
Get an API 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 Specification
openapi: 3.2.0
info:
title: Hail Sms 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: SMS
paths:
/v1/sms:
post:
tags:
- SMS
summary: Create Sms
description: 'Send an outbound SMS.
Sends synchronously — the response reports the final status (sent or
failed), not a queued placeholder, though delivery confirmation from
the carrier can still arrive later as a webhook or GET /v1/sms/{sms_id}
update. An explicit from resolves a dedicated number; otherwise Hail
picks an alphanumeric sender ID where the destination corridor allows
it, or requires a dedicated SMS-capable number otherwise. Requires
recipient_consent=true on the request body; Hail does not verify
lawful basis to contact the recipient, the caller warrants it.'
operationId: create_sms_v1_sms_post
parameters:
- name: authorization
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Authorization
- name: Idempotency-Key
in: header
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Idempotency-Key
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SmsCreate'
responses:
'201':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/SmsResponse'
'429':
description: Rate limited. The agent-origin workspace exceeded a per-channel velocity cap, or the platform kill switch is on. Retry after the Retry-After header (seconds). 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
'402':
description: insufficient credits; top up at https://hail.so/console/billing
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
get:
tags:
- SMS
summary: List Sms
description: 'List SMS messages for the caller''s organization, newest first.
Cursor-paginated: pass the returned next_cursor to fetch the next page;
a null next_cursor means there are no more results. Filter by status or
destination number (to) to narrow the list.'
operationId: list_sms_v1_sms_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: status
in: query
required: false
schema:
anyOf:
- enum:
- queued
- sent
- delivered
- failed
- undelivered
- received
type: string
- type: 'null'
title: Status
- name: to
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: To
- 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/SmsListResponse'
'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/sms/suppressions:
get:
tags:
- SMS
summary: List Sms Suppressions
description: 'List SMS numbers suppressed from receiving messages, newest first.
Cursor-paginated. A suppressed number blocks POST /v1/sms sends to it
with a 403 until removed via DELETE /v1/sms/suppressions/{number}.'
operationId: list_sms_suppressions_v1_sms_suppressions_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/SuppressionListResponse'
'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/sms/suppressions/{number}:
delete:
tags:
- SMS
summary: Delete Sms Suppression
description: 'Remove one number from the SMS suppression list, re-allowing sends to it.
Does not itself constitute renewed consent — the caller is responsible
for having a lawful basis (e.g. a fresh opt-in) before sending again.
Returns 404 if the number was not suppressed.'
operationId: delete_sms_suppression_v1_sms_suppressions__number__delete
parameters:
- name: number
in: path
required: true
schema:
type: string
title: Number
- 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/sms/sender-id:
get:
tags:
- SMS
summary: Get Sender Id
description: 'Get the org''s custom alphanumeric SMS sender ID, if any is set.
effective_default is the platform sender id used for
alphanumeric-eligible corridors when custom_sender_id is null.'
operationId: get_sender_id_v1_sms_sender_id_get
parameters:
- 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/SenderIdResponse'
'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:
- SMS
summary: Patch Sender Id
description: 'Set or clear the org''s custom alphanumeric SMS sender ID.
Pass custom_sender_id=null to clear it and fall back to the platform
default sender id. Only affects alphanumeric-eligible corridors — a
corridor requiring a dedicated number is unaffected.'
operationId: patch_sender_id_v1_sms_sender_id_patch
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/SenderIdPatch'
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/SenderIdResponse'
'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/sms/{sms_id}:
get:
tags:
- SMS
summary: Get Sms
description: 'Fetch one SMS by id, including its current status.
Org-scoped: returns 404 for an SMS belonging to a different
organization.'
operationId: get_sms_v1_sms__sms_id__get
parameters:
- name: sms_id
in: path
required: true
schema:
type: string
format: uuid
title: Sms 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/SmsResponse'
'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:
SmsCreate:
properties:
recipient_consent:
type: boolean
title: Recipient Consent
description: Attestation that you have obtained the lawful consent required to contact this recipient. Hail does not verify consent itself — you are responsible for a lawful basis under TCPA/ePrivacy/PECR/CAN-SPAM/GDPR as applicable. Rejected (422) if not true.
consent_source:
anyOf:
- type: string
- type: 'null'
title: Consent Source
description: Where/how consent was obtained (e.g. 'signup form', 'prior customer relationship'). Required (non-empty) when message_type is 'marketing'.
consent_obtained_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Consent Obtained At
description: When consent was obtained, if known.
message_type:
type: string
enum:
- marketing
- informational
title: Message Type
description: '''marketing'' additionally requires a non-empty consent_source. Use ''informational'' for transactional/service communications.'
default: informational
to:
type: string
title: To
description: Recipient phone number, E.164 format (e.g. +14155551234).
from:
anyOf:
- type: string
- type: 'null'
title: From
description: 'Sender phone number, E.164 format. Must be an active number owned by the organization with the SMS capability. Omitted: destinations where the sender-ID carrier delivers a dynamic alphanumeric sender ID unchanged (about 110 countries: most of Europe, Africa, the Middle East, Asia-Pacific and the Caribbean; one table per carrier in core/hailhq/core/sender_id_corridors.py) use the organization''s sender ID, or the platform default ''HAIL'' when none is set; Australia (+61) may always use ''HAIL'', depending on the carrier. Every other destination, including the US and Canada, uses the organization''s oldest active SMS-capable number; if none exists the request fails with 422. SMS never uses the shared pool.'
body:
type: string
maxLength: 1600
minLength: 1
title: Body
description: Message text. Long bodies are split into multiple carrier segments.
metadata:
additionalProperties: true
type: object
title: Metadata
description: Free-form JSON object attached to the message and echoed back on reads. Not interpreted by Hail.
additionalProperties: false
type: object
required:
- recipient_consent
- to
- body
title: SmsCreate
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
SenderIdPatch:
properties:
custom_sender_id:
anyOf:
- type: string
- type: 'null'
title: Custom Sender Id
description: Alphanumeric sender id (2-11 characters, letters/digits only) to use on alphanumeric-eligible corridors instead of a phone number. Explicit null clears it, reverting to the platform default.
additionalProperties: false
type: object
title: SenderIdPatch
SmsResponse:
properties:
id:
type: string
format: uuid
title: Id
description: Unique identifier for this message.
organization_id:
type: string
format: uuid
title: Organization Id
description: Organization that sent or received this message.
from_e164:
type: string
title: From E164
description: Sender phone number, E.164 format.
to_e164:
type: string
title: To E164
description: Recipient phone number, E.164 format.
direction:
type: string
enum:
- outbound
- inbound
title: Direction
description: '''outbound'' for messages Hail sent, ''inbound'' for messages received.'
agent_id:
anyOf:
- type: string
format: uuid
- type: 'null'
title: Agent Id
description: 'The agent this text belongs to: the number''s text agent on inbound texts, the agent that wrote it on outbound texts. Null when no agent is involved.'
status:
type: string
enum:
- queued
- sent
- delivered
- failed
- undelivered
- received
title: Status
description: 'Delivery status: ''queued'', ''sent'', ''delivered'', ''failed'', ''undelivered'', or ''received'' (inbound messages).'
body:
type: string
title: Body
description: Message text.
provider_message_sid:
anyOf:
- type: string
- type: 'null'
title: Provider Message Sid
description: The carrier/provider's identifier for this message, if assigned.
segment_count:
type: integer
title: Segment Count
description: Number of carrier SMS segments the body was split into.
error_code:
anyOf:
- type: string
- type: 'null'
title: Error Code
description: Carrier error code if delivery failed. Null on success or while pending.
requested_at:
type: string
format: date-time
title: Requested At
description: When the send was requested, ISO 8601 timestamp.
sent_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Sent At
description: When the message was handed to the carrier, ISO 8601 timestamp. Null until sent.
type: object
required:
- id
- organization_id
- from_e164
- to_e164
- direction
- status
- body
- provider_message_sid
- segment_count
- error_code
- requested_at
- sent_at
title: SmsResponse
SenderIdResponse:
properties:
custom_sender_id:
anyOf:
- type: string
- type: 'null'
title: Custom Sender Id
description: The organization's configured alphanumeric sender id. Null if none is set.
effective_default:
type: string
title: Effective Default
description: The platform's alphanumeric sender id, used on eligible corridors when custom_sender_id is null.
default: HAIL
type: object
required:
- custom_sender_id
title: SenderIdResponse
SuppressionListResponse:
properties:
items:
items:
$ref: '#/components/schemas/SuppressionResponse'
type: array
title: Items
description: Suppressed recipients 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: SuppressionListResponse
SmsListResponse:
properties:
items:
items:
$ref: '#/components/schemas/SmsResponse'
type: array
title: Items
description: Messages in this page, newest first.
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: SmsListResponse
SuppressionResponse:
properties:
id:
type: string
format: uuid
title: Id
description: Unique identifier for this suppression entry.
recipient:
type: string
title: Recipient
description: The suppressed recipient — E.164 phone number for voice/sms, lowercased email address for email.
channel:
type: string
title: Channel
description: 'Channel this entry blocks sends on: ''voice'', ''email'', ''sms'', or ''all'' (every channel).'
reason:
type: string
title: Reason
description: Why the recipient was suppressed (e.g. an unsubscribe or a bounce).
source:
type: string
title: Source
description: 'How this entry was created: ''unsubscribe_link'' (email unsubscribe link) or ''stop_keyword'' (recipient replied STOP by SMS). ''manual'' (operator action) and ''bounce'' (bounce handler) are reserved; nothing writes them yet.'
created_at:
type: string
format: date-time
title: Created At
description: When this entry was created, ISO 8601 timestamp.
type: object
required:
- id
- recipient
- channel
- reason
- source
- created_at
title: SuppressionResponse