Skip to content

SMS

SMS sending, OTP and verification, and delivery status.

1. Overview

Base path: https://api.infrai.cc/v1/sms
Auth header: Authorization: Bearer $INFRAI_API_KEY
bash
# Call any /v1/sms capability over raw HTTP — no SDK to install.
# curl:
curl https://api.infrai.cc/v1/sms/... \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json"

2. Methods (21)

2.1sms.send

POST /v1/sms/send

Send an SMS message; supports templates.

Parameters

NameTypeRequiredDescription
tostring
Required
Recipient phone number in E.164.
bodystringOptionalMessage text.
fromstringOptionalSender id or number.
template_idstringOptionalTemplate id to render instead of a body.
vendorstringOptionalPin a specific vendor instead of auto-routing.
idempotency_keystringOptionalOptional dedup key; identical retries return the same result.

Returns

SmsRecord { sms_id, to, state, vendor, segments, created_at }
NameTypeDescription
message_idstringUnique identifier for this message
pattern: ^sms_[A-Za-z0-9]{20,}$
state"queued" | "sending" | "sent" | "delivered" | "failed" | "deferred" | "expired" | "cancelled" | "auto_suppressed"Current lifecycle state of this resource
vendorstringVendor that handled or will handle this request
segmentsintegerLong SMS multi-part billing count.
≥ 1
cost_usdnumber | nullCost of this operation in USD
≥ 0
created_atstringISO 8601 timestamp when this resource was created
format: date-time

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X POST https://api.infrai.cc/v1/sms/send \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": "+14155550100", "body": "Your code is 123456"}'

2.2sms.otp

POST /v1/sms/otp

Send a one-time passcode to a phone number.

Parameters

NameTypeRequiredDescription
tostring
Required
Recipient phone number in E.164.
e.g. +15555550100

Returns

{ request_id, expires_at }
NameTypeDescription
message_idstringUnique identifier for this message
pattern: ^sms_[A-Za-z0-9]{20,}$
state"queued" | "sending" | "sent" | "delivered" | "failed" | "deferred" | "expired" | "cancelled" | "auto_suppressed"Current lifecycle state of this resource
vendorstringVendor that handled or will handle this request
segmentsintegerLong SMS multi-part billing count.
≥ 1
cost_usdnumber | nullCost of this operation in USD
≥ 0
created_atstringISO 8601 timestamp when this resource was created
format: date-time

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X POST https://api.infrai.cc/v1/sms/otp \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": "+15555550100"}'

2.3sms.verify

POST /v1/sms/verify

Verify a one-time passcode.

Parameters

NameTypeRequiredDescription
tostring
Required
Recipient phone number in E.164.
codestring
Required
The one-time passcode to verify.

Returns

{ valid: boolean }
NameTypeDescription
verifiedbooleanWhether the verification succeeded
reason"verified" | "no_code_issued" | "expired" | "too_many_attempts" | "mismatch"Outcome reason: 'verified' on success; otherwise why verification failed
tostringPhone number that was checked

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X POST https://api.infrai.cc/v1/sms/verify \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": "sample", "code": "123456"}'

2.4sms.status

GET /v1/sms/status/{id}

Fetch the delivery status of an SMS.

Parameters

NameTypeRequiredDescription
idstring
Required
The SMS record id.

Returns

SmsRecord
NameTypeDescription
idstringMessage id (send returns e.g. 'sms_…'); the archive stores it under 'id'.
statusstringLifecycle state: one of enums/SmsState (queued|sending|sent|delivered|failed|deferred|expired|cancelled|auto_suppressed) or 'not_found' when the id is unknown.
channel"sms" | "email" | nullAlways 'sms' for this capability.
account_idstring | nullAccount identifier that owns this resource
toanyRecipient(s) as supplied at send time.
vendorstring | nullVendor that handled the send; null on a not-found read.
vendor_message_idstring | nullMessage identifier assigned by the upstream vendor
created_atstring | nullISO 8601 timestamp when this resource was created
format: date-time
foundbooleantrue when the id resolved to an archived message; false on a miss. (A miss should raise a typed 404 once SMS_MESSAGE_NOT_FOUND lands — see module notes.)
state"queued" | "sending" | "sent" | "delivered" | "failed" | "deferred" | "expired" | "cancelled" | "auto_suppressed"Current lifecycle state of this resource
attemptinteger | nullCurrent attempt number (for retries)
≥ 0
last_eventstring | nullDescription of the most recent event
delivered_atstring | nullISO 8601 timestamp when the message was delivered
format: date-time
failed_reasonstring | nullReason for failure, if the operation failed

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X GET https://api.infrai.cc/v1/sms/status/ID \
  -H "Authorization: Bearer $INFRAI_API_KEY"

2.5sms.batch.send

POST /v1/sms/batch/send

Heterogeneous batch sending: Each message is a complete SMS request, the entire batch shares an idempotent key, and billing is not repeated.

Parameters

NameTypeRequiredDescription
messagesSmsSendRequest[]
Required
Array of SMS requests, up to 100 messages, each one is a complete sending request.
1–100 items
idempotency_keystringOptionalDeduplication key shared by the entire batch; same retry returns the same result.

Returns

SmsBatchSendResult { results: SmsSendResult[] }
NameTypeDescription
resultsobject[]List of individual results from a batch operation
results[].indexintegerPosition in the request messages array.
≥ 0
results[].resultobject | nullSend result when this item succeeded.
results[].result.message_idstringUnique identifier for this message
pattern: ^sms_[A-Za-z0-9]{20,}$
results[].result.state"queued" | "sending" | "sent" | "delivered" | "failed" | "deferred" | "expired" | "cancelled" | "auto_suppressed"Current lifecycle state of this resource
results[].result.vendorstringVendor that handled or will handle this request
results[].result.segmentsintegerLong SMS multi-part billing count.
≥ 1
results[].result.cost_usdnumber | nullCost of this operation in USD
≥ 0
results[].result.created_atstringISO 8601 timestamp when this resource was created
format: date-time
results[].errorobject | nullError when this item failed; result is null.
results[].error.codestringStable identifier from registry.yaml.
results[].error.http_statusintegerHTTP status code of the webhook delivery attempt
400–599
results[].error.messagestringHuman-readable hint.
results[].error.docs_urlstringDocumentation URL for this error code
format: uri
results[].error.code_detailstringSub-classification (e.g. wallet cap reason).
results[].error.retryablebooleanWhether the error is retryable
results[].error.retry_after_msintegerSuggested retry delay in milliseconds
≥ 0
results[].error.paramstringWhich parameter failed validation, if applicable.
results[].error.trace_idstringDistributed tracing identifier
results[].error.request_idstringServer-assigned request identifier for tracing

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X POST https://api.infrai.cc/v1/sms/batch/send \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"to": "sample"}]}'

2.6sms.cancel

POST /v1/sms/cancel/{id}

Cancel scheduled or queued text messages that have not yet been sent.

Parameters

NameTypeRequiredDescription
idstring
Required
SMS message ID to cancel.

Returns

SmsCancelResult { message_id, cancelled, state }
NameTypeDescription
message_idstringUnique identifier for this message
pattern: ^sms_[A-Za-z0-9]{20,}$
cancelledbooleanWhether the resource was successfully cancelled
state"queued" | "sending" | "sent" | "delivered" | "failed" | "deferred" | "expired" | "cancelled" | "auto_suppressed"Current lifecycle state of this resource

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X POST https://api.infrai.cc/v1/sms/cancel/ID \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message_id": "hello"}'

2.7sms.events

GET /v1/sms/events/{id}

Read the delivery event timeline of a single text message.

Parameters

NameTypeRequiredDescription
idstring
Required
SMS message ID.
cursorstringOptionalPaging cursor, obtained from next_cursor of the previous page.
limitnumberOptionalThe maximum number of items returned on a single page.

Returns

SmsEventList { items: SmsEvent[], next_cursor }
NameTypeDescription
itemsobject[]Array of result items in this page
items[].type"queued" | "sending" | "sent" | "delivered" | "failed" | "deferred" | "expired" | "cancelled" | "auto_suppressed" | "delivery_receipt" | "inbound"Type discriminator for this resource
items[].atstringISO 8601 timestamp of the event
format: date-time
items[].recipientstringE.164 phone number.
items[].message_idstring | nullUnique identifier for this message
pattern: ^sms_[A-Za-z0-9]{20,}$
items[].metaobject | nullAdditional metadata for the event
next_cursorstring | nullOpaque cursor to fetch the next page; null/absent if this is the last page

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X GET https://api.infrai.cc/v1/sms/events/ID \
  -H "Authorization: Bearer $INFRAI_API_KEY"

2.8sms.inbound.list

GET /v1/sms/inbound/list

Paginated list of inbound (reply) text messages received on registered sending numbers.

Parameters

NameTypeRequiredDescription
cursorstringOptionalPaging cursor, obtained from next_cursor of the previous page.
limitnumberOptionalThe maximum number of items returned on a single page.

Returns

SmsListResult { items, next_cursor }
NameTypeDescription
itemsobject[]Array of result items in this page
items[].type"queued" | "sending" | "sent" | "delivered" | "failed" | "deferred" | "expired" | "cancelled" | "auto_suppressed" | "delivery_receipt" | "inbound"Type discriminator for this resource
items[].atstringISO 8601 timestamp of the event
format: date-time
items[].recipientstringE.164 phone number.
items[].message_idstring | nullUnique identifier for this message
pattern: ^sms_[A-Za-z0-9]{20,}$
items[].metaobject | nullAdditional metadata for the event
next_cursorstring | nullOpaque cursor to fetch the next page; null/absent if this is the last page

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X GET https://api.infrai.cc/v1/sms/inbound/list \
  -H "Authorization: Bearer $INFRAI_API_KEY"

2.9sms.resend

POST /v1/sms/resend/{id}

Resend the text message, optionally retrying only failed recipients.

Parameters

NameTypeRequiredDescription
message_idstring
Required
The original SMS message ID to be resent.
pattern: ^sms_[A-Za-z0-9]{20,}$
only_failedbooleanOptionalWhen true, only failed delivery recipients are retried.
default: true
idempotency_keystringOptionalDeduplication key; same retry returns the same result.

Returns

SmsBatchSendResult { results }
NameTypeDescription
message_idstringUnique identifier for this message
pattern: ^sms_[A-Za-z0-9]{20,}$
state"queued" | "sending" | "sent" | "delivered" | "failed" | "deferred" | "expired" | "cancelled" | "auto_suppressed"Current lifecycle state of this resource
vendorstringVendor that handled or will handle this request
segmentsintegerLong SMS multi-part billing count.
≥ 1
cost_usdnumber | nullCost of this operation in USD
≥ 0
created_atstringISO 8601 timestamp when this resource was created
format: date-time

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X POST https://api.infrai.cc/v1/sms/resend/ID \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message_id": "hello"}'

2.10sms.signature.create

POST /v1/sms/signature/create

Create an SMS signature (required for sending from China) and submit it for review.

Parameters

NameTypeRequiredDescription
namestring
Required
Signature content text.
type"company" | "app" | "website" | "public_account" | "store" | "other"OptionalSignature type, such as enterprise, individual.
default: "company"
proof_urlstringOptionalURL of qualification document.
format: uri
remarkstringOptionalNotes when submitting for review.
vendorstringOptionalPin a specific vendor instead of auto-routing.

Returns

SmsSignature { signature_id, text, type, review_state, reject_reason, vendor, created_at }
NameTypeDescription
signature_idstringUnique identifier for this SMS signature
namestringSignature text shown as the 【signature】 prefix.
type"company" | "app" | "website" | "public_account" | "store" | "other"Signature scene (China vendor signature source).
default: "company"
review_state"pending" | "approved" | "rejected" | "n/a"China signatures always start 'pending' review; 'n/a' for non-China.
default: "pending"
review_reasonstring | nullVendor reviewer note when review_state=rejected.
proof_urlstring | nullURL of the ownership-proof document submitted to the China vendor.
vendor_sign_idstring | nullVendor-assigned sign id (Tencent SignId / Aliyun SignName) remembered after a real submit.
created_atstringISO 8601 timestamp when this resource was created
format: date-time
updated_atstring | nullISO 8601 timestamp when this resource was last updated
format: date-time
foundbooleanPresent on sms.signature.get; true when the signature exists. (A missing signature should raise a typed 404 once the SMS_SIGNATURE_NOT_FOUND code lands — see module notes.)

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X POST https://api.infrai.cc/v1/sms/signature/create \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme", "type": "company"}'

2.11sms.signature.get

GET /v1/sms/signature/get/{id}

Read a single SMS signature and its review status.

Parameters

NameTypeRequiredDescription
idstring
Required
Signature ID.

Returns

SmsSignature
NameTypeDescription
signature_idstringUnique identifier for this SMS signature
namestringSignature text shown as the 【signature】 prefix.
type"company" | "app" | "website" | "public_account" | "store" | "other"Signature scene (China vendor signature source).
default: "company"
review_state"pending" | "approved" | "rejected" | "n/a"China signatures always start 'pending' review; 'n/a' for non-China.
default: "pending"
review_reasonstring | nullVendor reviewer note when review_state=rejected.
proof_urlstring | nullURL of the ownership-proof document submitted to the China vendor.
vendor_sign_idstring | nullVendor-assigned sign id (Tencent SignId / Aliyun SignName) remembered after a real submit.
created_atstringISO 8601 timestamp when this resource was created
format: date-time
updated_atstring | nullISO 8601 timestamp when this resource was last updated
format: date-time
foundbooleanPresent on sms.signature.get; true when the signature exists. (A missing signature should raise a typed 404 once the SMS_SIGNATURE_NOT_FOUND code lands — see module notes.)

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X GET https://api.infrai.cc/v1/sms/signature/get/ID \
  -H "Authorization: Bearer $INFRAI_API_KEY"

2.12sms.signature.list

GET /v1/sms/signature/list

List SMS signatures in pages.

Parameters

NameTypeRequiredDescription
cursorstringOptionalPaging cursor, obtained from next_cursor of the previous page.
limitnumberOptionalThe maximum number of items returned on a single page.

Returns

SmsSignatureListResult { items, next_cursor }
NameTypeDescription
itemsobject[]Array of result items in this page
items[].signature_idstringUnique identifier for this SMS signature
items[].namestringSignature text shown as the 【signature】 prefix.
items[].type"company" | "app" | "website" | "public_account" | "store" | "other"Signature scene (China vendor signature source).
default: "company"
items[].review_state"pending" | "approved" | "rejected" | "n/a"China signatures always start 'pending' review; 'n/a' for non-China.
default: "pending"
items[].review_reasonstring | nullVendor reviewer note when review_state=rejected.
items[].proof_urlstring | nullURL of the ownership-proof document submitted to the China vendor.
items[].vendor_sign_idstring | nullVendor-assigned sign id (Tencent SignId / Aliyun SignName) remembered after a real submit.
items[].created_atstringISO 8601 timestamp when this resource was created
format: date-time
items[].updated_atstring | nullISO 8601 timestamp when this resource was last updated
format: date-time
items[].foundbooleanPresent on sms.signature.get; true when the signature exists. (A missing signature should raise a typed 404 once the SMS_SIGNATURE_NOT_FOUND code lands — see module notes.)
next_cursorstring | nullOpaque cursor to fetch the next page; null/absent if this is the last page
countintegerTotal number of items across all pages
≥ 0

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X GET https://api.infrai.cc/v1/sms/signature/list \
  -H "Authorization: Bearer $INFRAI_API_KEY"

2.13sms.signature.delete

DELETE /v1/sms/signature/delete/{id}

Delete an SMS signature.

Parameters

NameTypeRequiredDescription
idstring
Required
Signature ID to delete.
idempotency_keystringOptionalDeduplication key; same retry returns the same result.

Returns

SmsDeleteResult { deleted }
NameTypeDescription
signature_idstring | nullIdentifier of the deleted signature
deletedbooleanWhether the signature was deleted
foundbooleanWhether a signature with this id existed

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X DELETE https://api.infrai.cc/v1/sms/signature/delete/ID \
  -H "Authorization: Bearer $INFRAI_API_KEY"

2.14sms.template.create

POST /v1/sms/template/create

Create reusable SMS templates with named variables.

Parameters

NameTypeRequiredDescription
namestring
Required
Template name.
bodystring
Required
Template text, which can contain {variable} placeholders.
localestringOptionalTemplate language area, such as en, zh.
default: "en"
variablesstring[]OptionalList of variable names used in the template.
vendorstringOptionalPin a specific vendor instead of auto-routing.

Returns

SmsTemplate { template_id, name, body, locale, variables, review_state, created_at }
NameTypeDescription
template_idstringIdentifier of the template used for rendering
namestringHuman-readable name for this resource
bodystringPlain text body of the message
localestringLocale code for the template (e.g. en-US, zh-CN)
default: "en"
variablesstring[]Template variable definitions
review_state"pending" | "approved" | "rejected" | "n/a"Required for China vendors; n/a otherwise.
default: "n/a"
review_reasonstring | nullVendor reviewer note when review_state=rejected.
vendor_template_idstring | nullVendor-assigned template id remembered after a real submit (polled by query_review_state).
scope"account" | "platform"Template visibility: account-private or platform-shared.
default: "account"
vendorstring | nullSMS vendor that owns the vendor_template_id.
message_type"otp" | "notification" | "marketing" | nullTemplate category requested at creation time.
default: "otp"
international0 | 1 | nullTencent template region flag saved for vendor review polling: 0 mainland China, 1 international/HK/Macau/Taiwan.
created_atstringISO 8601 timestamp when this resource was created
format: date-time
updated_atstring | nullISO 8601 timestamp when this resource was last updated
format: date-time
foundbooleanPresent on sms.template.get; true when the template exists. (A missing template should raise a typed 404 once the SMS_TEMPLATE_NOT_FOUND code lands — see module notes.)

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X POST https://api.infrai.cc/v1/sms/template/create \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "otp", "body": "Your code is {{code}}", "locale": "zh"}'

2.15sms.template.get

GET /v1/sms/template/get/{id}

Read a single SMS template.

Parameters

NameTypeRequiredDescription
idstring
Required
Template ID.

Returns

SmsTemplate
NameTypeDescription
template_idstringIdentifier of the template used for rendering
namestringHuman-readable name for this resource
bodystringPlain text body of the message
localestringLocale code for the template (e.g. en-US, zh-CN)
default: "en"
variablesstring[]Template variable definitions
review_state"pending" | "approved" | "rejected" | "n/a"Required for China vendors; n/a otherwise.
default: "n/a"
review_reasonstring | nullVendor reviewer note when review_state=rejected.
vendor_template_idstring | nullVendor-assigned template id remembered after a real submit (polled by query_review_state).
scope"account" | "platform"Template visibility: account-private or platform-shared.
default: "account"
vendorstring | nullSMS vendor that owns the vendor_template_id.
message_type"otp" | "notification" | "marketing" | nullTemplate category requested at creation time.
default: "otp"
international0 | 1 | nullTencent template region flag saved for vendor review polling: 0 mainland China, 1 international/HK/Macau/Taiwan.
created_atstringISO 8601 timestamp when this resource was created
format: date-time
updated_atstring | nullISO 8601 timestamp when this resource was last updated
format: date-time
foundbooleanPresent on sms.template.get; true when the template exists. (A missing template should raise a typed 404 once the SMS_TEMPLATE_NOT_FOUND code lands — see module notes.)

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X GET https://api.infrai.cc/v1/sms/template/get/ID \
  -H "Authorization: Bearer $INFRAI_API_KEY"

2.16sms.template.list

GET /v1/sms/template/list

List SMS templates in pages.

Parameters

NameTypeRequiredDescription
cursorstringOptionalPaging cursor, obtained from next_cursor of the previous page.
limitnumberOptionalThe maximum number of items returned on a single page.

Returns

SmsTemplateListResult { items, next_cursor }
NameTypeDescription
itemsobject[]Array of result items in this page
items[].template_idstringIdentifier of the template used for rendering
items[].namestringHuman-readable name for this resource
items[].bodystringPlain text body of the message
items[].localestringLocale code for the template (e.g. en-US, zh-CN)
default: "en"
items[].variablesstring[]Template variable definitions
items[].review_state"pending" | "approved" | "rejected" | "n/a"Required for China vendors; n/a otherwise.
default: "n/a"
items[].review_reasonstring | nullVendor reviewer note when review_state=rejected.
items[].vendor_template_idstring | nullVendor-assigned template id remembered after a real submit (polled by query_review_state).
items[].scope"account" | "platform"Template visibility: account-private or platform-shared.
default: "account"
items[].vendorstring | nullSMS vendor that owns the vendor_template_id.
items[].message_type"otp" | "notification" | "marketing" | nullTemplate category requested at creation time.
default: "otp"
items[].international0 | 1 | nullTencent template region flag saved for vendor review polling: 0 mainland China, 1 international/HK/Macau/Taiwan.
items[].created_atstringISO 8601 timestamp when this resource was created
format: date-time
items[].updated_atstring | nullISO 8601 timestamp when this resource was last updated
format: date-time
items[].foundbooleanPresent on sms.template.get; true when the template exists. (A missing template should raise a typed 404 once the SMS_TEMPLATE_NOT_FOUND code lands — see module notes.)
next_cursorstring | nullOpaque cursor to fetch the next page; null/absent if this is the last page
countintegerTotal number of items across all pages
≥ 0

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X GET https://api.infrai.cc/v1/sms/template/list \
  -H "Authorization: Bearer $INFRAI_API_KEY"

2.17sms.template.delete

DELETE /v1/sms/template/delete/{id}

Delete an SMS template.

Parameters

NameTypeRequiredDescription
idstring
Required
Template ID to delete.
idempotency_keystringOptionalDeduplication key; same retry returns the same result.

Returns

SmsDeleteResult { deleted }
NameTypeDescription
template_idstring | nullIdentifier of the deleted template
deletedbooleanWhether the template was deleted
foundbooleanWhether a template with this id existed

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X DELETE https://api.infrai.cc/v1/sms/template/delete/ID \
  -H "Authorization: Bearer $INFRAI_API_KEY"

2.18sms.suppression.add

POST /v1/sms/suppression/add

Add the number to the suppress (do not disturb) list.

Parameters

NameTypeRequiredDescription
phonestring
Required
Phone number to suppress (E.164 format).
reason"user_request" | "stop_reply" | "carrier_block" | "invalid" | "manual"OptionalSuppression reasons such as opt_out, complaint.
default: "manual"
scope"account"OptionalSuppression scope, such as account, global.
default: "account"
notesstringOptionalInternal notes.
idempotency_keystringOptionalDeduplication key; same retry returns the same result.

Returns

SuppressionRecord { phone, scope, reason, created_at }
NameTypeDescription
phonestringE.164 phone.
reason"user_request" | "stop_reply" | "carrier_block" | "invalid" | "manual"Reason for the current state or action
added_atstringISO 8601 timestamp when this resource was added
format: date-time
scope"account"Scope of applicability for this resource
last_attempt_atstring | nullISO 8601 timestamp of the most recent blocked send attempt, if any.
format: date-time
attempt_count_blockedintegerCount of send attempts blocked by this suppression entry so far.
≥ 0
notesstring | nullFree-form notes attached when the entry was added.

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X POST https://api.infrai.cc/v1/sms/suppression/add \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone": "+15555550100"}'

2.19sms.suppression.check

POST /v1/sms/suppression/check

Check if the number has been suppressed before sending.

Parameters

NameTypeRequiredDescription
phonestring
Required
The phone number to check (E.164 format).
scope"account"OptionalCheck the suppression scope, such as account, global.
default: "account"

Returns

SmsSuppressionCheckResult { suppressed, record }
NameTypeDescription
phonestringE.164 phone that was checked.
suppressedbooleanWhether the address is currently suppressed
reason"user_request" | "stop_reply" | "carrier_block" | "invalid" | "manual"Suppression reason; present only when suppressed=true
added_atstringISO 8601 timestamp when the entry was added; present only when suppressed=true
format: date-time
scope"account"Scope of the matching entry; present only when suppressed=true
last_attempt_atstring | nullISO 8601 timestamp of the most recent blocked send attempt, if any
format: date-time
attempt_count_blockedintegerCount of send attempts blocked by this suppression entry so far; present only when suppressed=true
≥ 0
notesstring | nullFree-form notes attached when the entry was added

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X POST https://api.infrai.cc/v1/sms/suppression/check \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone": "+15555550100"}'

2.20sms.suppression.list

GET /v1/sms/suppression/list

Paginated list of suppressed numbers.

Parameters

NameTypeRequiredDescription
cursorstringOptionalPaging cursor, obtained from next_cursor of the previous page.
limitnumberOptionalThe maximum number of items returned on a single page.

Returns

SmsSuppressionListResult { items, total_count, next_cursor }
NameTypeDescription
itemsobject[]Array of result items in this page
items[].phonestringE.164 phone.
items[].reason"user_request" | "stop_reply" | "carrier_block" | "invalid" | "manual"Reason for the current state or action
items[].added_atstringISO 8601 timestamp when this resource was added
format: date-time
items[].scope"account"Scope of applicability for this resource
items[].last_attempt_atstring | nullISO 8601 timestamp of the most recent blocked send attempt, if any.
format: date-time
items[].attempt_count_blockedintegerCount of send attempts blocked by this suppression entry so far.
≥ 0
items[].notesstring | nullFree-form notes attached when the entry was added.
countinteger | nullTotal number of items across all pages
≥ 0
total_countinteger | nullTotal number of items across all pages
≥ 0
next_cursorstring | nullOpaque cursor to fetch the next page; null/absent if this is the last page

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X GET https://api.infrai.cc/v1/sms/suppression/list \
  -H "Authorization: Bearer $INFRAI_API_KEY"

2.21sms.suppression.delete

POST /v1/sms/suppression/delete

Remove the number from the suppression list to resume sending.

Parameters

NameTypeRequiredDescription
phonestring
Required
Phone number to remove (E.164 format).
scope"account"OptionalRemoved suppression scope, such as account, global.
default: "account"
idempotency_keystringOptionalDeduplication key; same retry returns the same result.

Returns

SmsSuppressionRemoveResult { phone, removed }
NameTypeDescription
phonestringE.164 phone targeted.
deletedbooleanWhether the resource was successfully removed
foundbooleanWhether a matching entry existed

Example

One-time prep (each example is assumed to be complete):

bash
# No SDK to install — every call is a plain HTTPS request.
# Get a project key by signing in at https://infrai.cc/login (Google/GitHub gives
# you $2 free credit; email sign-in starts at $0). On 402 INSUFFICIENT_CREDIT, add
# funds at https://infrai.cc/billing (or POST /v1/account/topup and open the
# returned checkout_url).
export INFRAI_API_KEY="ifr_..."
bash
curl -X POST https://api.infrai.cc/v1/sms/suppression/delete \
  -H "Authorization: Bearer $INFRAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone": "+15555550100"}'
Advanced: pin a vendor

By default infrai routes each call to the best available provider — you do not pick a vendor. As an escape hatch, this capability accepts an optional vendor parameter to pin one specific provider. Every live vendor for this capability is available in real time from the discovery endpoint for the capability id — see the discovery API.

GET /v1/discovery/{capability}

sms.send

sms.signature.create

sms.template.create

3. All capabilities

Every routed capability in this module — the complete public REST contract. The methods above are the guided walkthrough; this index is the full reference.

sms.batch.sendPOST /v1/sms/batch/send

Send a heterogeneous batch of SMS (each a full request) under one idempotency key, billed per message.

Parameters (2)
NameTypeRequiredDescription
messagesobject[]RequiredUp to 100 SmsSendRequest items.
1–100 items
idempotency_keystring | nullOptionalClient-provided idempotency key; prevents duplicate execution on retry
sms.cancelPOST /v1/sms/cancel/{id}

Cancel a scheduled or queued SMS that has not yet been dispatched.

Parameters (3)
NameTypeRequiredDescription
idstringRequiredPath parameter.
message_idstringRequiredId of the SMS message to cancel.
pattern: ^sms_[A-Za-z0-9]{20,}$
idempotency_keystring | nullOptionalClient-provided idempotency key; prevents duplicate execution on retry
sms.eventsGET /v1/sms/events/{id}

Retrieve the delivery event timeline for a single SMS message.

Parameters (1)
NameTypeRequiredDescription
idstringRequiredPath parameter.
sms.inbound.listGET /v1/sms/inbound/list

List inbound (reply) SMS received on registered sender numbers, with pagination.

No request parameters.

sms.otpPOST /v1/sms/otp

Send a one-time passcode via SMS with gateway-managed OTP lifecycle.

Parameters (4)
NameTypeRequiredDescription
tostringRequiredDestination phone number (E.164).
e.g. +15555550100
phonestring | nullOptionalLegacy alias for to.
templatestring | nullOptionalMessage template containing {code}; defaults to 'Your verification code is {code}'.
idempotency_keystring | nullOptionalClient-provided idempotency key; prevents duplicate execution on retry
sms.resendPOST /v1/sms/resend/{id}

Resend an SMS, optionally retrying only the failed recipients.

Parameters (4)
NameTypeRequiredDescription
idstringRequiredPath parameter.
message_idstringRequiredUnique identifier for this message
pattern: ^sms_[A-Za-z0-9]{20,}$
only_failedbooleanOptionalRe-dispatch only failed recipients; false resends the whole original recipient set.
default: true
idempotency_keystring | nullOptionalClient-provided idempotency key; prevents duplicate execution on retry
sms.sendPOST /v1/sms/send

Send an SMS message to one or more recipients (idempotent).

Parameters (12)
NameTypeRequiredDescription
tostring | string[]RequiredE.164 phone number or array of.
bodystring | nullOptionalPlain text body of the message
fromstring | nullOptionalSender ID, phone number, or approved sign name to use when the vendor path supports it.
template_idstring | nullOptionalIdentifier of the template used for rendering
template_varsobject | nullOptionalVariables to substitute in the template
vendorstring | nullOptionalPin to a specific vendor.
mode"default_vendor"OptionalDelivery mode or operation mode
default: "default_vendor"
scheduled_atstring | nullOptionalISO 8601 timestamp when the resource is scheduled to activate
format: date-time
idempotency_keystring | nullOptionalClient-provided idempotency key; prevents duplicate execution on retry
tagsstring[]OptionalTags for categorization and filtering
route_class"transactional" | "otp" | "marketing"OptionalSMS route class for delivery optimization
default: "transactional"
opt_in_proof_urlstring | nullOptionalRequired when route_class=marketing.
format: uri
sms.signature.createPOST /v1/sms/signature/create

Create an SMS signature (required for China delivery) and submit it for review.

Parameters (5)
NameTypeRequiredDescription
namestringRequiredSignature text shown as the bracketed prefix, e.g. 'Acme'.
type"company" | "app" | "website" | "public_account" | "store" | "other"OptionalSignature usage scenario required by China vendors.
default: "company"
proof_urlstring | nullOptionalURL to the authorization letter / business-license image required for brand review.
format: uri
remarkstring | nullOptionalNote shown to the vendor reviewer.
vendorstring | nullOptionalPin the vendor to register at; otherwise the gateway picks per region.
sms.signature.deleteDELETE /v1/sms/signature/delete/{id}

Delete a registered SMS signature (China-route vendor pre-config); sends referencing it will be rejected.

Parameters (1)
NameTypeRequiredDescription
idstringRequiredPath parameter.
sms.signature.getGET /v1/sms/signature/get/{id}

Retrieve a single SMS signature and its review status.

Parameters (1)
NameTypeRequiredDescription
idstringRequiredPath parameter.
sms.signature.listGET /v1/sms/signature/list

List SMS signatures with pagination.

No request parameters.

sms.statusGET /v1/sms/status/{id}

Query the delivery status of an SMS message by ID.

Parameters (1)
NameTypeRequiredDescription
idstringRequiredPath parameter.
sms.suppression.addPOST /v1/sms/suppression/add

Add a phone number to the suppression (do-not-contact) list.

Parameters (5)
NameTypeRequiredDescription
phonestringRequiredE.164 phone.
reason"user_request" | "stop_reply" | "carrier_block" | "invalid" | "manual"OptionalReason for the current state or action
default: "manual"
scope"account"OptionalScope of applicability for this resource
default: "account"
notesstring | nullOptionalFree-text notes about this suppression
idempotency_keystring | nullOptionalClient-provided idempotency key; prevents duplicate execution on retry
sms.suppression.checkPOST /v1/sms/suppression/check

Check whether a phone number is suppressed before sending.

Parameters (2)
NameTypeRequiredDescription
phonestringRequiredE.164 phone.
scope"account"OptionalScope of applicability for this resource
default: "account"
sms.suppression.deletePOST /v1/sms/suppression/delete

Remove a number from the suppression list to resume sending.

Parameters (3)
NameTypeRequiredDescription
phonestringRequiredE.164 phone.
scope"account"OptionalScope of applicability for this resource
default: "account"
idempotency_keystring | nullOptionalClient-provided idempotency key; prevents duplicate execution on retry
sms.suppression.listGET /v1/sms/suppression/list

List suppressed phone numbers with pagination.

No request parameters.

sms.template.createPOST /v1/sms/template/create

Create a reusable SMS template with named variables.

Parameters (7)
NameTypeRequiredDescription
namestringRequiredHuman label for the template.
bodystringRequiredTemplate text with {{var}} placeholders. Variable names may contain only letters, digits and underscore, and each variable may appear at most once.
localestringOptionalzh/cn locales trigger China-vendor review.
default: "en"
variablesstring[]OptionalDeclared placeholder names. If provided, it must exactly match the placeholders in body; final order is inferred from first appearance in body.
vendorstring | nullOptionalPin the vendor to register at; otherwise the gateway picks per region.
message_type"otp" | "notification" | "marketing"OptionalTemplate category. Infrai maps this to the vendor-native template type, e.g. Tencent SmsType: otp=3, notification=2, marketing=1.
default: "otp"
international0 | 1OptionalTencent template region flag: 0 mainland China, 1 international/HK/Macau/Taiwan.
default: 1
sms.template.deleteDELETE /v1/sms/template/delete/{id}

Delete a registered SMS message template (vendor pre-config); sends referencing it will be rejected.

Parameters (1)
NameTypeRequiredDescription
idstringRequiredPath parameter.
sms.template.getGET /v1/sms/template/get/{id}

Retrieve a single SMS template.

Parameters (1)
NameTypeRequiredDescription
idstringRequiredPath parameter.
sms.template.listGET /v1/sms/template/list

List SMS templates with pagination.

No request parameters.

sms.verifyPOST /v1/sms/verify

Verify a user-submitted SMS one-time passcode.

Parameters (5)
NameTypeRequiredDescription
tostringRequiredPhone number the OTP was issued to (E.164).
phonestring | nullOptionalLegacy alias for to.
codestringRequiredThe one-time code to verify.
otpstring | nullOptionalLegacy alias for code.
idempotency_keystring | nullOptionalClient-provided idempotency key; prevents duplicate execution on retry

4. End-to-end example

A production-style walkthrough of this module: configure once, then run the flow. It exercises most of the module's APIs.

A copy-paste-runnable single-file Python program (stdlib only, no SDK): set your INFRAI_API_KEY, run it, and walk this module's core flow with REAL billed calls — later steps reuse real fields returned by earlier ones. The 12-line helper is the entire integration.

python
#!/usr/bin/env python3
"""Infrai · sms — runnable real-app example (single file, zero deps).

Copy this file, set your key, run it: every step is a REAL call to
api.infrai.cc, billed at the real (tiny) per-call price, printing the
live JSON response. Get a key at https://infrai.cc/login (Google/
GitHub sign-in grants $2 free credit); add funds at
https://infrai.cc/billing. No SDK — the 12-line helper below is the
entire integration."""
import json
import os
from urllib import error, request

KEY = os.environ.get("INFRAI_API_KEY") or "ifr_..."  # <- your key
BASE = "https://api.infrai.cc"


# Same raw HTTPS POST/GET as every per-method example on this page —
# wrapped once for reuse. There is nothing else to it: no SDK.
def infrai(method, path, body=None):
    req = request.Request(
        BASE + path, method=method,
        data=json.dumps(body).encode() if body is not None else None,
        headers={"Authorization": f"Bearer {KEY}",
                 "Content-Type": "application/json"})
    try:
        with request.urlopen(req, timeout=60) as r:
            return json.loads(r.read())
    except error.HTTPError as e:
        return json.loads(e.read())


def show(label, resp):
    print(f"\n== {label} ==")
    print(json.dumps(resp, indent=2, ensure_ascii=False))
    return resp


# 1) sms.otp — POST /v1/sms/otp · Send a one-time passcode via SMS with gateway-managed OTP lifecycle.
ask1 = input("Your phone number, intl format (+86...): ").strip()
r1 = show("sms.otp", infrai("POST", "/v1/sms/otp", {"to":ask1}))

# 2) sms.verify — POST /v1/sms/verify · Verify a user-submitted SMS one-time passcode.
ask2 = input("Enter the code you just received: ").strip()
r2 = show("sms.verify", infrai("POST", "/v1/sms/verify", {"to":ask1,"code":ask2}))

5. Developer guides

Move from endpoint details to complete workflows, production patterns and troubleshooting guides verified against the live API.

SMS developer guides