API documentation
Everything this website does, your code or an AI agent can do with an API key: get a number, wait for its text, extend it or cancel it. Create a key on the Developers page.
Base URLhttps://api.verifywith.io/v1
Get started
Three calls take you from a key to a code. Put your key in VERIFYWITH_API_KEY first.
1. Get a number
Buys a number for a day with your saved card, within the key’s limits. Keep the Idempotency-Key: sending the same request with it again never buys twice. The response has the number in number.phoneNumber and its id in number.id. If it answers 202 with order.nextAction, the card holder must confirm with their bank at that link; then send the same request again.
curl -X POST https://api.verifywith.io/v1/numbers \
-H "Authorization: Bearer $VERIFYWITH_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"duration": "day"}'2. Wait for the code
Waits for the next text (timeoutSeconds, 60 by default, at most 90) and answers with it and the code found in it (code). On "status": "timeout", call again; pass the last message id you saw as afterMessageId so a text is never missed.
curl "https://api.verifywith.io/v1/numbers/NUMBER_ID/messages/next?timeoutSeconds=60" \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
3. Cancel it, if no text came
In its first 24 hours and before any text, a number can be cancelled; when our provider accepts, everything paid for it comes back. When it refuses (not_cancellable), the number stays yours until it ends.
curl -X POST https://api.verifywith.io/v1/numbers/NUMBER_ID/cancel \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
API keys and limits
Create a key on the Developers page and send it as a bearer token. The whole key is shown once, when you create it. Use one key per program, so you can revoke one without stopping the others.
What a key may do
- Read only
- Lists numbers and reads their texts and codes. It can’t change or buy anything.
- Manage
- Also renames numbers, turns auto-renew on or off, cancels, and manages webhooks.
- Full access
- Also gets new numbers and extends them, charged to your saved card within the key’s limits.
A full-access key has two spending limits you set: the most it may pay for one number, and the most it may spend in a day. A request over either limit is refused with spending_limit_exceeded and nothing is charged. Change the limits or revoke the key at any time; both apply from its next request.
curl https://api.verifywith.io/v1/numbers \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
Connect an AI agent
AI agents connect over MCP (Model Context Protocol). Most sign in through the browser: the app opens this website, you choose what it may do and spend, and it appears on the Developers page beside your keys, where you change its limits or disconnect it.
Programs that can’t sign in use an API key sent as Authorization: Bearer sk_…. Either way the level and spending limits apply to everything the agent does, and revoking stops it at once.
https://api.verifywith.io/mcp
Claude, on the web and in the desktop app
Customize, then Connectors, then Add custom connector, with the server address. Claude opens this site to sign in.
ChatGPT
Plugins, then Add custom MCP server, with the server address and OAuth. ChatGPT opens this site to sign in.
Claude Code
Add the server, then run /mcp in Claude Code (or claude mcp login verifywith) to sign in.
claude mcp add --transport http verifywith https://api.verifywith.io/mcp
Cursor
In ~/.cursor/mcp.json, or .cursor/mcp.json in a project, then sign in from Cursor’s MCP settings.
{
"mcpServers": {
"verifywith": {
"url": "https://api.verifywith.io/mcp"
}
}
}Your own code, with a key
Any MCP client that can send an Authorization: Bearer sk_… header. In Claude Code, for example.
claude mcp add --transport http verifywith https://api.verifywith.io/mcp \ --header "Authorization: Bearer sk_..."
The tools an agent gets
list_prices- The durations and prices, and this connection’s level and limits.
buy_number- Buys a number. A repeated identical call resumes the same purchase instead of buying again.
wait_for_code- Waits up to 55 seconds for the next text, and answers with it and its code.
list_numbers- The person’s numbers.
get_number_details- One number, including whether and until when it can be cancelled.
read_messages- The texts on a number, newest first.
extend_number- Adds time, charged at once.
set_auto_renew- Turns renewing on or off.
cancel_number- Cancels, with the money back when our provider accepts.
When the bank wants to confirm a payment, or there is no card to pay with, the agent gets a link for its person and continues once they have done it.
Receive webhooks
Instead of waiting on a request, your server can be told. Add an endpoint (an https address) on the Developers page or with the API, and choose the events it wants. We send each one as a JSON POST. Answer with any 2xx within 10 seconds.
Events
message.received- A text arrives. Carries the message, with its code when one was found.
number.active- A number is ready to use.
number.failed- We couldn’t get a number.
number.renewed- A number renewed.
number.renewal_failed- A renewal payment failed.
number.expired- A number ended.
number.cancelled- A number was cancelled.
payment.action_required- The bank wants the card holder to confirm a payment.
{
"id": "evt_msg_665f1c2a9b3e4d0012a7c3f2",
"type": "message.received",
"createdAt": "2026-11-12T10:00:00.000Z",
"data": {
"number": {
"id": "665f1c2a9b3e4d0012a7c3f1",
"phoneNumber": "+16285550113",
"…": "the whole Number object"
},
"message": {
"id": "665f1c2a9b3e4d0012a7c3f2",
"sender": "WhatsApp",
"body": "Your WhatsApp code is 482-913.",
"code": "482913"
}
}
}Check that it came from us
Requests follow the Standard Webhooks spec, so its libraries work as they are. Each request carries webhook-id, webhook-timestamp and webhook-signature. The signature is v1, then the base64 HMAC-SHA256 of id.timestamp.body, using the raw body and the endpoint’s signing secret (the part after whsec_, base64-decoded). The secret is shown once, when you add the endpoint.
Refuse a request whose timestamp is more than a few minutes old, and use webhook-id to ignore an event you have already handled.
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody: the request body exactly as received
export function cameFromUs(headers, rawBody, secret) {
const key = Buffer.from(secret.replace("whsec_", ""), "base64");
const signed = `${headers["webhook-id"]}.${headers["webhook-timestamp"]}.${rawBody}`;
const ours = createHmac("sha256", key).update(signed).digest("base64");
return headers["webhook-signature"].split(" ").some((part) => {
const theirs = Buffer.from(part.replace("v1,", ""));
return theirs.length === ours.length &&
timingSafeEqual(theirs, Buffer.from(ours));
});
}When your server doesn’t answer
We try up to eight times over about a day, waiting longer each time. Every attempt and what your server answered is listed under the endpoint on the Developers page, where you can send an event again. An endpoint that keeps failing is turned off.
How the API works
Private, receive-only US phone numbers for verification texts. Everything the website does, your code or agent can do with an API key.
Authenticate with Authorization: Bearer sk_... (create keys on the Developers page). Key levels are cumulative: read; manage (rename, auto-renew, cancel, webhooks); buy (get and extend numbers, within the key's spending limits). Every buying call needs an Idempotency-Key header. Lists are paged with limit and cursor (pass nextCursor). Every error is { "error": code, "message": sentence, "requestId": id, "url"?: link }; url is where the person must act in a browser. Money is integer cents in USD; dates are ISO 8601 in UTC; ids are ours (24 hex characters).
The machine-readable description (OpenAPI 3.1), for generating a client, is at https://api.verifywith.io/v1/openapi.json.
curl https://api.verifywith.io/v1/numbers \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"error": "not_found",
"message": "There is no number with that id.",
"requestId": "req_7f3a2c1e"
}Numbers
List durations and prices
GET/v1/prices
The four durations on sale and what each costs now. No sign-in needed.
No key needed.
Returns
200- Prices in force now, shortest first: a page of Price
Errors
invalid_requestrate_limitedinternal_error
curl https://api.verifywith.io/v1/prices
{
"object": "list",
"data": [
{
"object": "price",
"id": "665f1c2a9b3e4d0012a7c3f1",
"duration": "day",
"label": "string",
"amount": 1299,
"currency": "usd",
"version": 0,
"activeFrom": "2026-11-12T10:00:00.000Z",
"renewable": false
}
]
}Get a number
POST/v1/numbers
Buys a private US number for the chosen duration. With an API key, the saved card is charged (held until the first text) within the key's spending limits. 201: paid and ordered; the number is active, or provisioning for a few seconds. 202 with order.nextAction: the bank wants the card holder to confirm; hand them the link, then repeat this request with the same Idempotency-Key. 202 with checkout (website only): mount the embedded payment form, then call confirm. Repeating a request with the same Idempotency-Key never buys twice.
Needs a full-access key. Charged within the key’s spending limits.
Parameters
Idempotency-Keyheader- string, 8 to 255 charactersAny unique string (8 to 255 printable characters) you choose per purchase. Repeating a request with the same key returns the same purchase instead of buying again, for 24 hours. Reusing a key for a different request answers
idempotency_key_reused.
Body
duration- one of day, week, month, year
autoRenewoptional- boolean, default false
nicknameoptional- string, 1 to 100 characters
cardConsentTextoptional- string, 10 to 2000 characters
Returns
201- Paid and ordered
202- The person must act first (see
checkoutandorder.nextAction)
Errors
invalid_requestunauthenticatedcard_requiredpayment_declinedaccount_suspendedinsufficient_scopeidempotency_key_reusedidempotency_in_progressspending_limit_exceededauto_renew_unavailablerate_limitedinternal_errorsupplier_unavailablesales_paused
curl -X POST https://api.verifywith.io/v1/numbers \
-H "Authorization: Bearer $VERIFYWITH_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"duration":"day"}'{
"order": {
"object": "order",
"id": "665f1c2a9b3e4d0012a7c3f1",
"status": "pending_payment",
"duration": "day",
"amount": 1299,
"currency": "usd",
"autoRenew": false,
"numberId": "665f1c2a9b3e4d0012a7c3f1",
"paymentId": "665f1c2a9b3e4d0012a7c3f1",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"failure": {
"code": "482913",
"message": "What went wrong, in a sentence a person can read."
},
"nextAction": {
"type": "confirm_payment",
"url": "https://your-app.example/hooks"
},
"createdAt": "2026-11-12T10:00:00.000Z",
"completedAt": "2026-11-12T10:00:00.000Z"
},
"number": {
"object": "number",
"id": "665f1c2a9b3e4d0012a7c3f1",
"phoneNumber": "+16285550113",
"displayNumber": "(628) 555-0113",
"status": "active",
"state": "waiting_for_text",
"duration": "day",
"startsAt": "2026-11-12T10:00:00.000Z",
"endsAt": "2026-11-13T10:00:00.000Z",
"autoRenew": false,
"renewDuration": "day",
"renewalAmount": 1299,
"currency": "usd",
"nickname": "Work chat",
"messageCount": 1,
"firstMessageAt": "2026-11-12T10:00:00.000Z",
"lastMessageAt": "2026-11-12T10:00:00.000Z",
"latestCode": "482913",
"cancellableFrom": "2026-11-12T10:00:00.000Z",
"cancellableUntil": "2026-11-13T10:00:00.000Z",
"lastCheckedAt": "2026-11-12T10:00:00.000Z",
"services": [
"string"
],
"endedAt": "2026-11-12T10:00:00.000Z",
"endReason": "expired",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
},
"checkout": {
"clientSecret": "string",
"expiresAt": "2026-11-13T10:00:00.000Z"
}
}List numbers
GET/v1/numbers
Newest first. Numbers whose payment never went through are left out.
Any key can call this.
Parameters
limitqueryoptional- integer, 1 to 100
cursorqueryoptional- string, 1 to 512 characters
livequeryoptional- one of true, false
Returns
200- A page of numbers: a page of Number
Errors
invalid_requestunauthenticatedinsufficient_scoperate_limitedinternal_error
curl https://api.verifywith.io/v1/numbers \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "list",
"data": [
{
"object": "number",
"id": "665f1c2a9b3e4d0012a7c3f1",
"phoneNumber": "+16285550113",
"displayNumber": "(628) 555-0113",
"status": "active",
"state": "waiting_for_text",
"duration": "day",
"startsAt": "2026-11-12T10:00:00.000Z",
"endsAt": "2026-11-13T10:00:00.000Z",
"autoRenew": false,
"renewDuration": "day",
"renewalAmount": 1299,
"currency": "usd",
"nickname": "Work chat",
"messageCount": 1,
"firstMessageAt": "2026-11-12T10:00:00.000Z",
"lastMessageAt": "2026-11-12T10:00:00.000Z",
"latestCode": "482913",
"cancellableFrom": "2026-11-12T10:00:00.000Z",
"cancellableUntil": "2026-11-13T10:00:00.000Z",
"lastCheckedAt": "2026-11-12T10:00:00.000Z",
"services": [
"string"
],
"endedAt": "2026-11-12T10:00:00.000Z",
"endReason": "expired",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
}
],
"nextCursor": null
}Continue a purchase after the payment step
POST/v1/orders/{orderId}/confirm
After the embedded payment form finished in the browser, or after the person confirmed with their bank, this moves the order on and answers where it stands, like getting the number did. Safe to call more than once.
Needs a full-access key. Charged within the key’s spending limits.
Parameters
orderIdin the path- string
Returns
201- Paid and ordered
202- Still waiting for the person
Errors
invalid_requestunauthenticatedpayment_declinedcard_requiredinsufficient_scopenot_foundrate_limitedinternal_errorsupplier_unavailable
curl -X POST https://api.verifywith.io/v1/orders/ORDER_ID/confirm \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"order": {
"object": "order",
"id": "665f1c2a9b3e4d0012a7c3f1",
"status": "pending_payment",
"duration": "day",
"amount": 1299,
"currency": "usd",
"autoRenew": false,
"numberId": "665f1c2a9b3e4d0012a7c3f1",
"paymentId": "665f1c2a9b3e4d0012a7c3f1",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"failure": {
"code": "482913",
"message": "What went wrong, in a sentence a person can read."
},
"nextAction": {
"type": "confirm_payment",
"url": "https://your-app.example/hooks"
},
"createdAt": "2026-11-12T10:00:00.000Z",
"completedAt": "2026-11-12T10:00:00.000Z"
},
"number": {
"object": "number",
"id": "665f1c2a9b3e4d0012a7c3f1",
"phoneNumber": "+16285550113",
"displayNumber": "(628) 555-0113",
"status": "active",
"state": "waiting_for_text",
"duration": "day",
"startsAt": "2026-11-12T10:00:00.000Z",
"endsAt": "2026-11-13T10:00:00.000Z",
"autoRenew": false,
"renewDuration": "day",
"renewalAmount": 1299,
"currency": "usd",
"nickname": "Work chat",
"messageCount": 1,
"firstMessageAt": "2026-11-12T10:00:00.000Z",
"lastMessageAt": "2026-11-12T10:00:00.000Z",
"latestCode": "482913",
"cancellableFrom": "2026-11-12T10:00:00.000Z",
"cancellableUntil": "2026-11-13T10:00:00.000Z",
"lastCheckedAt": "2026-11-12T10:00:00.000Z",
"services": [
"string"
],
"endedAt": "2026-11-12T10:00:00.000Z",
"endReason": "expired",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
},
"checkout": {
"clientSecret": "string",
"expiresAt": "2026-11-13T10:00:00.000Z"
}
}Get a number
GET/v1/numbers/{numberId}
Any key can call this.
Parameters
numberIdin the path- string
Returns
200- The number: Number
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundrate_limitedinternal_error
curl https://api.verifywith.io/v1/numbers/NUMBER_ID \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "number",
"id": "665f1c2a9b3e4d0012a7c3f1",
"phoneNumber": "+16285550113",
"displayNumber": "(628) 555-0113",
"status": "active",
"state": "waiting_for_text",
"duration": "day",
"startsAt": "2026-11-12T10:00:00.000Z",
"endsAt": "2026-11-13T10:00:00.000Z",
"autoRenew": false,
"renewDuration": "day",
"renewalAmount": 1299,
"currency": "usd",
"nickname": "Work chat",
"messageCount": 1,
"firstMessageAt": "2026-11-12T10:00:00.000Z",
"lastMessageAt": "2026-11-12T10:00:00.000Z",
"latestCode": "482913",
"cancellableFrom": "2026-11-12T10:00:00.000Z",
"cancellableUntil": "2026-11-13T10:00:00.000Z",
"lastCheckedAt": "2026-11-12T10:00:00.000Z",
"services": [
"string"
],
"endedAt": "2026-11-12T10:00:00.000Z",
"endReason": "expired",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
}Rename a number
PATCH/v1/numbers/{numberId}
Set a nickname, or null to remove it.
Needs a key that can manage numbers.
Parameters
numberIdin the path- string
Body
nickname- string or null
Returns
200- The number: Number
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundrate_limitedinternal_error
curl -X PATCH https://api.verifywith.io/v1/numbers/NUMBER_ID \
-H "Authorization: Bearer $VERIFYWITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"nickname":"Work chat"}'{
"object": "number",
"id": "665f1c2a9b3e4d0012a7c3f1",
"phoneNumber": "+16285550113",
"displayNumber": "(628) 555-0113",
"status": "active",
"state": "waiting_for_text",
"duration": "day",
"startsAt": "2026-11-12T10:00:00.000Z",
"endsAt": "2026-11-13T10:00:00.000Z",
"autoRenew": false,
"renewDuration": "day",
"renewalAmount": 1299,
"currency": "usd",
"nickname": "Work chat",
"messageCount": 1,
"firstMessageAt": "2026-11-12T10:00:00.000Z",
"lastMessageAt": "2026-11-12T10:00:00.000Z",
"latestCode": "482913",
"cancellableFrom": "2026-11-12T10:00:00.000Z",
"cancellableUntil": "2026-11-13T10:00:00.000Z",
"lastCheckedAt": "2026-11-12T10:00:00.000Z",
"services": [
"string"
],
"endedAt": "2026-11-12T10:00:00.000Z",
"endReason": "expired",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
}Turn auto-renew on or off
PUT/v1/numbers/{numberId}/auto-renew
Renewals repeat the duration bought at the price it was bought at. Choosing a different duration resets that locked price to today's price for it. Renewals never count against an API key's spending limits.
Needs a key that can manage numbers.
Parameters
numberIdin the path- string
Body
autoRenew- boolean
durationoptional- one of day, week, month, year
Returns
200- The number: Number
Errors
invalid_requestunauthenticatedcard_requiredinsufficient_scopenot_foundnumber_not_activenumber_busyauto_renew_unavailablerate_limitedinternal_error
curl -X PUT https://api.verifywith.io/v1/numbers/NUMBER_ID/auto-renew \
-H "Authorization: Bearer $VERIFYWITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"autoRenew":false}'{
"object": "number",
"id": "665f1c2a9b3e4d0012a7c3f1",
"phoneNumber": "+16285550113",
"displayNumber": "(628) 555-0113",
"status": "active",
"state": "waiting_for_text",
"duration": "day",
"startsAt": "2026-11-12T10:00:00.000Z",
"endsAt": "2026-11-13T10:00:00.000Z",
"autoRenew": false,
"renewDuration": "day",
"renewalAmount": 1299,
"currency": "usd",
"nickname": "Work chat",
"messageCount": 1,
"firstMessageAt": "2026-11-12T10:00:00.000Z",
"lastMessageAt": "2026-11-12T10:00:00.000Z",
"latestCode": "482913",
"cancellableFrom": "2026-11-12T10:00:00.000Z",
"cancellableUntil": "2026-11-13T10:00:00.000Z",
"lastCheckedAt": "2026-11-12T10:00:00.000Z",
"services": [
"string"
],
"endedAt": "2026-11-12T10:00:00.000Z",
"endReason": "expired",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
}Extend a number
POST/v1/numbers/{numberId}/extend
Adds the duration to the number's end date and charges the saved card at once (within the key's spending limits). 202 requires_action: hand the person nextAction.url, then repeat with the same Idempotency-Key.
Needs a full-access key. Charged within the key’s spending limits.
Parameters
numberIdin the path- string
Idempotency-Keyheader- string, 8 to 255 charactersAny unique string (8 to 255 printable characters) you choose per purchase. Repeating a request with the same key returns the same purchase instead of buying again, for 24 hours. Reusing a key for a different request answers
idempotency_key_reused.
Body
duration- one of day, week, month, year
Returns
200- Extended
202- Paid and being applied, or the person must confirm
Fields in the response
object- "extension"
status- one of extended, pending, requires_action
number- Number
payment- Payment
nextAction- NextAction or null
clientSecret- string or null
message- string or null
Errors
invalid_requestunauthenticatedcard_requiredpayment_declinedinsufficient_scopenot_foundnumber_not_activenumber_busyidempotency_key_reusedspending_limit_exceededrate_limitedinternal_errorsupplier_unavailable
curl -X POST https://api.verifywith.io/v1/numbers/NUMBER_ID/extend \
-H "Authorization: Bearer $VERIFYWITH_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"duration":"day"}'{
"object": "extension",
"status": "extended",
"number": {
"object": "number",
"id": "665f1c2a9b3e4d0012a7c3f1",
"phoneNumber": "+16285550113",
"displayNumber": "(628) 555-0113",
"status": "active",
"state": "waiting_for_text",
"duration": "day",
"startsAt": "2026-11-12T10:00:00.000Z",
"endsAt": "2026-11-13T10:00:00.000Z",
"autoRenew": false,
"renewDuration": "day",
"renewalAmount": 1299,
"currency": "usd",
"nickname": "Work chat",
"messageCount": 1,
"firstMessageAt": "2026-11-12T10:00:00.000Z",
"lastMessageAt": "2026-11-12T10:00:00.000Z",
"latestCode": "482913",
"cancellableFrom": "2026-11-12T10:00:00.000Z",
"cancellableUntil": "2026-11-13T10:00:00.000Z",
"lastCheckedAt": "2026-11-12T10:00:00.000Z",
"services": [
"string"
],
"endedAt": "2026-11-12T10:00:00.000Z",
"endReason": "expired",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
},
"payment": {
"object": "payment",
"id": "665f1c2a9b3e4d0012a7c3f1",
"kind": "purchase",
"status": "succeeded",
"amount": 1299,
"refundedAmount": 1299,
"currency": "usd",
"duration": "day",
"card": {
"brand": "visa",
"last4": "4242",
"expMonth": 4,
"expYear": 2030
},
"receiptUrl": "https://example.com",
"numberId": "665f1c2a9b3e4d0012a7c3f1",
"orderId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
},
"nextAction": {
"type": "confirm_payment",
"url": "https://your-app.example/hooks"
},
"clientSecret": "string",
"message": "What went wrong, in a sentence a person can read."
}Check for new texts now
POST/v1/numbers/{numberId}/messages/check
Asks the carrier for this number's texts now and stores any that were missed. Texts normally arrive by themselves within seconds; this is for a person (or an agent) who would rather not wait. One number is asked at most once every 15 seconds: a call in between answers at once with asked: false. Each account gets 12 checks a minute; beyond that, rate_limited with a Retry-After.
Any key can call this.
Parameters
numberIdin the path- string
Returns
200- What the check found
Fields in the response
object- "inbox_check"
asked- boolean
found- integer
nextCheckAt- string
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundnumber_not_activerate_limitedinternal_error
curl -X POST https://api.verifywith.io/v1/numbers/NUMBER_ID/messages/check \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "inbox_check",
"asked": false,
"found": 0,
"nextCheckAt": "2026-11-12T10:00:00.000Z"
}Try a declined renewal payment again
POST/v1/numbers/{numberId}/renewal/retry
After a renewal was declined (the number's state is renewal_payment_failed), charges the saved cards again for the same renewal, now, on the same payment, so it is never paid twice. Use it after the card holder fixed or added a card. 202 requires_action: the bank wants the card holder to confirm at nextAction.url. Renewals never count against an API key's spending limits.
Needs a full-access key. Charged within the key’s spending limits.
Parameters
numberIdin the path- string
Returns
200- Renewed
202- Paid and being applied, or the card holder must confirm
Fields in the response
object- "renewal_retry"
status- one of renewed, pending, requires_action
number- Number
paymentId- string or null
nextAction- NextAction or null
message- string or null
Errors
invalid_requestunauthenticatedcard_requiredpayment_declinedinsufficient_scopenot_foundconflictnumber_not_activenumber_busyrate_limitedinternal_errorsupplier_unavailable
curl -X POST https://api.verifywith.io/v1/numbers/NUMBER_ID/renewal/retry \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "renewal_retry",
"status": "renewed",
"number": {
"object": "number",
"id": "665f1c2a9b3e4d0012a7c3f1",
"phoneNumber": "+16285550113",
"displayNumber": "(628) 555-0113",
"status": "active",
"state": "waiting_for_text",
"duration": "day",
"startsAt": "2026-11-12T10:00:00.000Z",
"endsAt": "2026-11-13T10:00:00.000Z",
"autoRenew": false,
"renewDuration": "day",
"renewalAmount": 1299,
"currency": "usd",
"nickname": "Work chat",
"messageCount": 1,
"firstMessageAt": "2026-11-12T10:00:00.000Z",
"lastMessageAt": "2026-11-12T10:00:00.000Z",
"latestCode": "482913",
"cancellableFrom": "2026-11-12T10:00:00.000Z",
"cancellableUntil": "2026-11-13T10:00:00.000Z",
"lastCheckedAt": "2026-11-12T10:00:00.000Z",
"services": [
"string"
],
"endedAt": "2026-11-12T10:00:00.000Z",
"endReason": "expired",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
},
"paymentId": "665f1c2a9b3e4d0012a7c3f1",
"nextAction": {
"type": "confirm_payment",
"url": "https://your-app.example/hooks"
},
"message": "What went wrong, in a sentence a person can read."
}Cancel a number (with a refund when eligible)
POST/v1/numbers/{numberId}/cancel
Offered on numbers with no texts, in their first 24 hours, from cancellableFrom. We ask our provider first: when it accepts, everything paid for the number goes back (a hold is released, so it never shows as a charge). When it refuses, the number stays active and paid (not_cancellable).
Needs a key that can manage numbers.
Parameters
numberIdin the path- string
Body
reasonoptional- string, at most 500 characters
Returns
200- Cancelled
202- Our provider has not answered yet; we keep asking
Fields in the response
object- "cancellation"
status- one of cancelled, in_progress
number- Number
refund- object or null
message- string or null
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundnot_cancellablecancel_too_sooncancel_window_closednumber_not_activenumber_busyrate_limitedinternal_error
curl -X POST https://api.verifywith.io/v1/numbers/NUMBER_ID/cancel \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "cancellation",
"status": "cancelled",
"number": {
"object": "number",
"id": "665f1c2a9b3e4d0012a7c3f1",
"phoneNumber": "+16285550113",
"displayNumber": "(628) 555-0113",
"status": "active",
"state": "waiting_for_text",
"duration": "day",
"startsAt": "2026-11-12T10:00:00.000Z",
"endsAt": "2026-11-13T10:00:00.000Z",
"autoRenew": false,
"renewDuration": "day",
"renewalAmount": 1299,
"currency": "usd",
"nickname": "Work chat",
"messageCount": 1,
"firstMessageAt": "2026-11-12T10:00:00.000Z",
"lastMessageAt": "2026-11-12T10:00:00.000Z",
"latestCode": "482913",
"cancellableFrom": "2026-11-12T10:00:00.000Z",
"cancellableUntil": "2026-11-13T10:00:00.000Z",
"lastCheckedAt": "2026-11-12T10:00:00.000Z",
"services": [
"string"
],
"endedAt": "2026-11-12T10:00:00.000Z",
"endReason": "expired",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
},
"refund": {
"status": "returned",
"amount": 1299,
"released": false
},
"message": "What went wrong, in a sentence a person can read."
}Messages
List the texts a number received
GET/v1/numbers/{numberId}/messages
Newest first, with the full text and the code found in it.
Any key can call this.
Parameters
numberIdin the path- string
limitqueryoptional- integer, 1 to 100
cursorqueryoptional- string, 1 to 512 characters
Returns
200- A page of messages: a page of Message
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundrate_limitedinternal_error
curl https://api.verifywith.io/v1/numbers/NUMBER_ID/messages \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "list",
"data": [
{
"object": "message",
"id": "665f1c2a9b3e4d0012a7c3f1",
"numberId": "665f1c2a9b3e4d0012a7c3f1",
"sender": "WhatsApp",
"body": "Your WhatsApp code is 482-913. Don't share it.",
"code": "482913",
"receivedAt": "2026-11-12T10:00:00.000Z"
}
],
"nextCursor": null
}Wait for the next text and its code
GET/v1/numbers/{numberId}/messages/next
Holds the request open until a text arrives (then answers at once with it and its code) or until timeoutSeconds (at most 90) pass, then answers timeout. Pass the last message id you saw as afterMessageId so a text that arrived between two calls is never missed.
Any key can call this.
Parameters
numberIdin the path- string
timeoutSecondsqueryoptional- integer, 1 to 90
afterMessageIdqueryoptional- string
Returns
200- A text arrived, or the wait timed out
Fields in the response
object- "code_wait"
status- one of received, timeout
message- Message or null
code- string or null
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundnumber_not_activerate_limitedinternal_error
curl https://api.verifywith.io/v1/numbers/NUMBER_ID/messages/next \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "code_wait",
"status": "received",
"message": "What went wrong, in a sentence a person can read.",
"code": "482913"
}Say someone is looking at this number
POST/v1/numbers/{numberId}/watch
Call about every 30 seconds while a screen shows the number and waits for a text, when not using the live stream's watch. It makes us check with our provider if a text seems late.
Any key can call this.
Parameters
numberIdin the path- string
Returns
204- Noted
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundrate_limitedinternal_error
curl -X POST https://api.verifywith.io/v1/numbers/NUMBER_ID/watch \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
Account
Who is calling
GET/v1/me
The account, and either the website session or the API key in use.
Needs an API key.
Returns
200- The caller
Fields in the response
object- "me"
user- object
session- object or null
apiKey- object or null
Errors
invalid_requestunauthenticatedrate_limitedinternal_error
curl https://api.verifywith.io/v1/me \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "me",
"user": {
"id": "665f1c2a9b3e4d0012a7c3f1",
"email": "[email protected]",
"emailVerified": false,
"name": "Laptop scripts",
"createdAt": "2026-11-12T10:00:00.000Z"
},
"session": {
"expiresAt": "2026-11-13T10:00:00.000Z"
},
"apiKey": {
"id": "665f1c2a9b3e4d0012a7c3f1",
"name": "Laptop scripts",
"scopes": [
"read"
]
}
}Payments
Payment history
GET/v1/payments
Every charge against its number, newest first. Refunds are on the same payment (refundedAmount).
Any key can call this.
Parameters
limitqueryoptional- integer, 1 to 100
cursorqueryoptional- string, 1 to 512 characters
numberIdqueryoptional- string
Returns
200- A page of payments: a page of Payment
Errors
invalid_requestunauthenticatedinsufficient_scoperate_limitedinternal_error
curl https://api.verifywith.io/v1/payments \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "list",
"data": [
{
"object": "payment",
"id": "665f1c2a9b3e4d0012a7c3f1",
"kind": "purchase",
"status": "succeeded",
"amount": 1299,
"refundedAmount": 1299,
"currency": "usd",
"duration": "day",
"card": {
"brand": null,
"last4": null,
"expMonth": null,
"expYear": null
},
"receiptUrl": "https://example.com",
"numberId": "665f1c2a9b3e4d0012a7c3f1",
"orderId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
}
],
"nextCursor": null
}Cards
Saved cards
GET/v1/cards
In charging order: the default first, then backups in the order they are tried. fresh=true re-reads them from the payment processor.
Any key can call this.
Parameters
freshqueryoptional- one of true, false
Returns
200- Cards: a page of Card
Errors
invalid_requestunauthenticatedinsufficient_scoperate_limitedinternal_error
curl https://api.verifywith.io/v1/cards \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "list",
"data": [
{
"brand": "visa",
"last4": "4242",
"expMonth": 4,
"expYear": 2030,
"object": "card",
"id": "665f1c2a9b3e4d0012a7c3f1",
"isDefault": false,
"expiresBeforeRenewal": false
}
]
}Webhooks
List webhook endpoints
GET/v1/webhook-endpoints
Any key can call this.
Returns
200- Endpoints, newest first: a page of WebhookEndpoint
Errors
invalid_requestunauthenticatedinsufficient_scoperate_limitedinternal_error
curl https://api.verifywith.io/v1/webhook-endpoints \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "list",
"data": [
{
"object": "webhook_endpoint",
"id": "665f1c2a9b3e4d0012a7c3f1",
"url": "https://your-app.example/hooks",
"description": "string",
"events": [
"number.active"
],
"enabled": false,
"createdAt": "2026-11-12T10:00:00.000Z"
}
]
}Add a webhook endpoint
POST/v1/webhook-endpoints
We POST each subscribed event to the https URL, signed with the Standard Webhooks spec (webhook-id, webhook-timestamp, webhook-signature), retrying for about a day. The signing secret is shown once.
Needs a key that can manage numbers.
Body
url- URL, at most 2048 characters
events- list of one of number.active, number.failed, number.renewed, number.renewal_failed, number.expired, number.cancelled, message.received, payment.action_required
descriptionoptional- string, at most 200 characters
Returns
201- The endpoint and its signing secret (shown once)
Fields in the response
object- "webhook_endpoint"
id- string
url- URL
description- string or null
events- list of one of number.active, number.failed, number.renewed, number.renewal_failed, number.expired, number.cancelled, message.received, payment.action_required
enabled- boolean
createdAt- date-time
secret- string
Errors
invalid_requestunauthenticatedinsufficient_scopeconflictrate_limitedinternal_error
curl -X POST https://api.verifywith.io/v1/webhook-endpoints \
-H "Authorization: Bearer $VERIFYWITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://your-app.example/hooks","events":["number.active"]}'{
"object": "webhook_endpoint",
"id": "665f1c2a9b3e4d0012a7c3f1",
"url": "https://your-app.example/hooks",
"description": "string",
"events": [
"number.active"
],
"enabled": false,
"createdAt": "2026-11-12T10:00:00.000Z",
"secret": "string"
}Get a webhook endpoint
GET/v1/webhook-endpoints/{endpointId}
Any key can call this.
Parameters
endpointIdin the path- string
Returns
200- The endpoint: WebhookEndpoint
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundrate_limitedinternal_error
curl https://api.verifywith.io/v1/webhook-endpoints/ENDPOINT_ID \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "webhook_endpoint",
"id": "665f1c2a9b3e4d0012a7c3f1",
"url": "https://your-app.example/hooks",
"description": "string",
"events": [
"number.active"
],
"enabled": false,
"createdAt": "2026-11-12T10:00:00.000Z"
}Change a webhook endpoint
PATCH/v1/webhook-endpoints/{endpointId}
Turning an endpoint back on (enabled: true) also clears its failure count.
Needs a key that can manage numbers.
Parameters
endpointIdin the path- string
Body
urloptional- URL, at most 2048 characters
eventsoptional- list of one of number.active, number.failed, number.renewed, number.renewal_failed, number.expired, number.cancelled, message.received, payment.action_required
descriptionoptional- string or null
enabledoptional- boolean
Returns
200- The endpoint: WebhookEndpoint
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundrate_limitedinternal_error
curl -X PATCH https://api.verifywith.io/v1/webhook-endpoints/ENDPOINT_ID \
-H "Authorization: Bearer $VERIFYWITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://your-app.example/hooks","events":["number.active"]}'{
"object": "webhook_endpoint",
"id": "665f1c2a9b3e4d0012a7c3f1",
"url": "https://your-app.example/hooks",
"description": "string",
"events": [
"number.active"
],
"enabled": false,
"createdAt": "2026-11-12T10:00:00.000Z"
}Delete a webhook endpoint
DELETE/v1/webhook-endpoints/{endpointId}
Deliveries still waiting are dropped.
Needs a key that can manage numbers.
Parameters
endpointIdin the path- string
Returns
204- Deleted
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundrate_limitedinternal_error
curl -X DELETE https://api.verifywith.io/v1/webhook-endpoints/ENDPOINT_ID \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
Recent deliveries to an endpoint
GET/v1/webhook-endpoints/{endpointId}/deliveries
Newest first, up to limit (at most 100). Deliveries are kept 7 days.
Any key can call this.
Parameters
endpointIdin the path- string
limitqueryoptional- integer, 1 to 100
Returns
200- Deliveries: a page of WebhookDelivery
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundrate_limitedinternal_error
curl https://api.verifywith.io/v1/webhook-endpoints/ENDPOINT_ID/deliveries \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "list",
"data": [
{
"object": "webhook_delivery",
"id": "665f1c2a9b3e4d0012a7c3f1",
"endpointId": "665f1c2a9b3e4d0012a7c3f1",
"eventId": "665f1c2a9b3e4d0012a7c3f1",
"eventType": "number.active",
"status": "succeeded",
"attempts": 0,
"responseStatus": 0,
"error": "string",
"createdAt": "2026-11-12T10:00:00.000Z",
"lastAttemptAt": "2026-11-12T10:00:00.000Z",
"nextAttemptAt": "2026-11-12T10:00:00.000Z",
"deliveredAt": "2026-11-12T10:00:00.000Z"
}
]
}Send an event again
POST/v1/webhook-deliveries/{deliveryId}/resend
A new attempt within seconds, with the same webhook-id, so a receiver that already handled it can tell.
Needs a key that can manage numbers.
Parameters
deliveryIdin the path- string
Returns
202- Queued: WebhookDelivery
Errors
invalid_requestunauthenticatedinsufficient_scopenot_foundconflictrate_limitedinternal_error
curl -X POST https://api.verifywith.io/v1/webhook-deliveries/DELIVERY_ID/resend \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
{
"object": "webhook_delivery",
"id": "665f1c2a9b3e4d0012a7c3f1",
"endpointId": "665f1c2a9b3e4d0012a7c3f1",
"eventId": "665f1c2a9b3e4d0012a7c3f1",
"eventType": "number.active",
"status": "succeeded",
"attempts": 0,
"responseStatus": 0,
"error": "string",
"createdAt": "2026-11-12T10:00:00.000Z",
"lastAttemptAt": "2026-11-12T10:00:00.000Z",
"nextAttemptAt": "2026-11-12T10:00:00.000Z",
"deliveredAt": "2026-11-12T10:00:00.000Z"
}Connected apps
Read an app's request to connect
GET/v1/connect/request
Used by the website's consent page when an AI app connects through the browser (OAuth). request is the query string the sign-in server sent the browser to the page with.
No key needed.
Parameters
requestquery- string, 1 to 8192 characters
Returns
200- Who is asking, and what for
Fields in the response
object- "connect_request"
app- object
maxLevel- one of read, manage, buy
defaultLevel- one of read, manage, buy
defaultLimits- object
expiresAt- date-time
Errors
invalid_requestrequest_expiredrate_limitedinternal_error
curl https://api.verifywith.io/v1/connect/request
{
"object": "connect_request",
"app": {
"name": "Laptop scripts",
"clientId": "665f1c2a9b3e4d0012a7c3f1",
"identifiedBy": "metadata_document",
"host": "string",
"verified": false,
"redirectHost": "string"
},
"maxLevel": "read",
"defaultLevel": "read",
"defaultLimits": {
"perNumber": 1299,
"perDay": 4000
},
"expiresAt": "2026-11-13T10:00:00.000Z"
}Live
Live updates (server-sent events)
GET/v1/stream
One text/event-stream for the whole account: message.received (the number and the text) and number.updated (the number), then ready once caught up. Reconnect with Last-Event-ID and every event since is replayed first; resync means the gap was too long, so re-read your numbers. Comments (: ping) every 25 seconds keep proxies from closing it. At most 10 streams per person.
Any key can call this.
Parameters
watchqueryoptional- string, at most 200 characters
lastEventIdqueryoptional- string, at most 40 characters
Returns
200- An event stream. Each event's
datais one of these, itseventis thetype.
Errors
invalid_requestunauthenticatedinsufficient_scoperate_limitedinternal_error
curl https://api.verifywith.io/v1/stream \ -H "Authorization: Bearer $VERIFYWITH_API_KEY"
Objects
Price
object- "price"
id- string
duration- one of day, week, month, year
label- string
amount- integer
currency- "usd"
version- integer
activeFrom- date-time
renewable- boolean
{
"object": "price",
"id": "665f1c2a9b3e4d0012a7c3f1",
"duration": "day",
"label": "string",
"amount": 1299,
"currency": "usd",
"version": 0,
"activeFrom": "2026-11-12T10:00:00.000Z",
"renewable": false
}Order
object- "order"
id- string
status- one of pending_payment, submitting, provisioning, reconciling, completed, failed, abandoned
duration- one of day, week, month, year
amount- integer
currency- "usd"
autoRenew- boolean
numberId- string
paymentId- string or null
source- one of web, api, mcp
apiKeyId- string or null
failure- object or null
nextAction- NextAction or null
createdAt- date-time
completedAt- date-time or null
{
"object": "order",
"id": "665f1c2a9b3e4d0012a7c3f1",
"status": "pending_payment",
"duration": "day",
"amount": 1299,
"currency": "usd",
"autoRenew": false,
"numberId": "665f1c2a9b3e4d0012a7c3f1",
"paymentId": "665f1c2a9b3e4d0012a7c3f1",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"failure": {
"code": "482913",
"message": "What went wrong, in a sentence a person can read."
},
"nextAction": {
"type": "confirm_payment",
"url": "https://your-app.example/hooks"
},
"createdAt": "2026-11-12T10:00:00.000Z",
"completedAt": "2026-11-12T10:00:00.000Z"
}NextAction
type- one of confirm_payment, add_card
url- URL
{
"type": "confirm_payment",
"url": "https://your-app.example/hooks"
}Number
object- "number"
id- string
phoneNumber- string or null
displayNumber- string or null
status- one of provisioning, active, cancelling, cancelled, expired, failed
state- one of getting_number, waiting_for_text, code_arrived, more_messages, ending_soon, ended, renewal_payment_failed, cancelled, failed
duration- one of day, week, month, year
startsAt- date-time or null
endsAt- date-time or null
autoRenew- boolean
renewDuration- one of day, week, month, year
renewalAmount- integer
currency- "usd"
nickname- string or null
messageCount- integer
firstMessageAt- date-time or null
lastMessageAt- date-time or null
latestCode- string or null
cancellableFrom- date-time or null
cancellableUntil- date-time or null
lastCheckedAtoptional- date-time or null
servicesoptional- list of string
endedAt- date-time or null
endReason- one of expired, renewal_unpaid, cancelled, cancelled_by_staff, cancelled_by_supplier, supplier_failed, payment_failed or null
source- one of web, api, mcp
apiKeyId- string or null
createdAt- date-time
{
"object": "number",
"id": "665f1c2a9b3e4d0012a7c3f1",
"phoneNumber": "+16285550113",
"displayNumber": "(628) 555-0113",
"status": "active",
"state": "waiting_for_text",
"duration": "day",
"startsAt": "2026-11-12T10:00:00.000Z",
"endsAt": "2026-11-13T10:00:00.000Z",
"autoRenew": false,
"renewDuration": "day",
"renewalAmount": 1299,
"currency": "usd",
"nickname": "Work chat",
"messageCount": 1,
"firstMessageAt": "2026-11-12T10:00:00.000Z",
"lastMessageAt": "2026-11-12T10:00:00.000Z",
"latestCode": "482913",
"cancellableFrom": "2026-11-12T10:00:00.000Z",
"cancellableUntil": "2026-11-13T10:00:00.000Z",
"lastCheckedAt": "2026-11-12T10:00:00.000Z",
"services": [
"string"
],
"endedAt": "2026-11-12T10:00:00.000Z",
"endReason": "expired",
"source": "web",
"apiKeyId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
}Payment
object- "payment"
id- string
kind- one of purchase, renewal, extension
status- one of pending, requires_action, processing, authorized, succeeded, failed, canceled, partially_refunded, refunded
amount- integer
refundedAmount- integer
currency- "usd"
duration- one of day, week, month, year
card- CardSummary or null
receiptUrl- URL or null
numberId- string or null
orderId- string or null
createdAt- date-time
{
"object": "payment",
"id": "665f1c2a9b3e4d0012a7c3f1",
"kind": "purchase",
"status": "succeeded",
"amount": 1299,
"refundedAmount": 1299,
"currency": "usd",
"duration": "day",
"card": {
"brand": "visa",
"last4": "4242",
"expMonth": 4,
"expYear": 2030
},
"receiptUrl": "https://example.com",
"numberId": "665f1c2a9b3e4d0012a7c3f1",
"orderId": "665f1c2a9b3e4d0012a7c3f1",
"createdAt": "2026-11-12T10:00:00.000Z"
}CardSummary
brand- string
last4- string
expMonth- integer, 1 to 12
expYear- integer, 2000 to 2200
{
"brand": "visa",
"last4": "4242",
"expMonth": 4,
"expYear": 2030
}Message
object- "message"
id- string
numberId- string
sender- string
body- string
code- string or null
receivedAt- date-time
{
"object": "message",
"id": "665f1c2a9b3e4d0012a7c3f1",
"numberId": "665f1c2a9b3e4d0012a7c3f1",
"sender": "WhatsApp",
"body": "Your WhatsApp code is 482-913. Don't share it.",
"code": "482913",
"receivedAt": "2026-11-12T10:00:00.000Z"
}Card
brand- string
last4- string
expMonth- integer, 1 to 12
expYear- integer, 2000 to 2200
object- "card"
id- string
isDefault- boolean
expiresBeforeRenewal- boolean
{
"brand": "visa",
"last4": "4242",
"expMonth": 4,
"expYear": 2030,
"object": "card",
"id": "665f1c2a9b3e4d0012a7c3f1",
"isDefault": false,
"expiresBeforeRenewal": false
}ApiKey
object- "api_key"
id- string
kind- one of key, connection
name- string
prefix- string
scopes- list of one of read, manage, buy
limits- object
app- object or null
numbersBought- integer
createdAt- date-time
lastUsedAt- date-time or null
{
"object": "api_key",
"id": "665f1c2a9b3e4d0012a7c3f1",
"kind": "key",
"name": "Laptop scripts",
"prefix": "string",
"scopes": [
"read"
],
"limits": {
"perNumber": 1299,
"perDay": 4000
},
"app": {
"name": "Laptop scripts",
"clientId": "665f1c2a9b3e4d0012a7c3f1",
"identifiedBy": "metadata_document",
"host": "string",
"verified": false,
"redirectHost": "string"
},
"numbersBought": 0,
"createdAt": "2026-11-12T10:00:00.000Z",
"lastUsedAt": "2026-11-12T10:00:00.000Z"
}WebhookEndpoint
object- "webhook_endpoint"
id- string
url- URL
description- string or null
events- list of one of number.active, number.failed, number.renewed, number.renewal_failed, number.expired, number.cancelled, message.received, payment.action_required
enabled- boolean
createdAt- date-time
{
"object": "webhook_endpoint",
"id": "665f1c2a9b3e4d0012a7c3f1",
"url": "https://your-app.example/hooks",
"description": "string",
"events": [
"number.active"
],
"enabled": false,
"createdAt": "2026-11-12T10:00:00.000Z"
}WebhookDelivery
object- "webhook_delivery"
id- string
endpointId- string
eventId- string
eventType- one of number.active, number.failed, number.renewed, number.renewal_failed, number.expired, number.cancelled, message.received, payment.action_required
status- one of pending, retrying, succeeded, failed
attempts- integer
responseStatus- integer or null
error- string or null
createdAt- date-time
lastAttemptAt- date-time or null
nextAttemptAt- date-time or null
deliveredAt- date-time or null
{
"object": "webhook_delivery",
"id": "665f1c2a9b3e4d0012a7c3f1",
"endpointId": "665f1c2a9b3e4d0012a7c3f1",
"eventId": "665f1c2a9b3e4d0012a7c3f1",
"eventType": "number.active",
"status": "succeeded",
"attempts": 0,
"responseStatus": 0,
"error": "string",
"createdAt": "2026-11-12T10:00:00.000Z",
"lastAttemptAt": "2026-11-12T10:00:00.000Z",
"nextAttemptAt": "2026-11-12T10:00:00.000Z",
"deliveredAt": "2026-11-12T10:00:00.000Z"
}Error
error- one of invalid_request, unauthenticated, card_required, payment_declined, forbidden, insufficient_scope, account_suspended, not_found, conflict, card_in_use, request_expired, idempotency_key_reused, idempotency_in_progress, not_cancellable, cancel_too_soon, cancel_window_closed, number_not_active, number_busy, auto_renew_unavailable, spending_limit_exceeded, purchase_limit_exceeded, rate_limited, internal_error, supplier_unavailable, sales_paused
message- string
requestId- string
urloptional- URL
{
"error": "invalid_request",
"message": "What went wrong, in a sentence a person can read.",
"requestId": "req_7f3a2c1e",
"url": "https://your-app.example/hooks"
}