# Instasent - transactional-api (full documentation) > Autocontained dump of every page under /a2p-messaging-api. Paste this into an AI assistant or feed it to an agent as context for transactional-api integration work. Some behaviour is cross-API: when a page here refers to another API or a shared concept, consult the zone index at https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms.txt (and, for product or dashboard questions, the whole-site index at https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt). Source: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/ Zone index: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms.txt OpenAPI spec: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/openapi/transactional.openapi.yaml --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/overview # A2P Messaging API Send messages straight from your code — built for developers and AI agents. A simple REST API (and SMPP) with delivery tracking and webhooks: SMS today, with RCS and WhatsApp coming soon. No audience to manage: bring your recipients and send. **Language:** en **Audience:** developer **TLDR:** The A2P Messaging API sends messages from your code: SMS today, RCS and WhatsApp coming soon. One at a time (POST /sms) or in batches (POST /sms/bulk), with DLRs pushed to your webhook, two-way inbound, message history, HLR lookups, balance and price profiles, over HTTP or SMPP. It is the Transactional API under its newer name: same endpoints, tokens and URLs. Use it when you manage your own recipient list; if Instasent should own the audience, use the platform. **Search keywords:** a2p messaging api, a2p, transactional api, sms api, smpp, bulk sms, dlr, hlr lookup **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/overview/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/overview.md (Markdown) > **Note**: **The Transactional API and the A2P Messaging API are the same API** — and, > before both, the SMS API. Same endpoints, same tokens, same URLs, nothing to > migrate: if your integration calls the Transactional API, it is already > calling this. The dashboard uses the newer name, so what you create there is > an *A2P Messaging API* token or project. > > It is also the **evolution of the [Legacy API](/a2p-messaging-api/legacy/overview)**: > the same endpoint surface, modernised with DLR webhooks, per-project tokens, > SMPP and the `api_sms` token model. If you are about to write new code, write > it against this API. The A2P Messaging API is how your code sends messages through Instasent. It connects your application to carrier networks, delivering to handsets in 200+ countries, and covers everything around the send: single or batched sending, DLRs and inbound, message history, HLR lookups, price profiles and account balance. Today it sends **SMS**; **RCS and WhatsApp are coming soon**. Two transports expose the same underlying pipeline. Pick the one that fits your stack — most teams start with HTTP and only move to SMPP when volume or session-state requirements justify it. - [HTTP (REST)](/a2p-messaging-api/http/quickstart) - JSON over HTTPS. Best for application servers, serverless functions and anything that already speaks REST. Webhook-based DLRs. - [SMPP](/a2p-messaging-api/smpp/integration) - Persistent TCP sessions, binary protocol. Best for wholesale SMS and high-throughput platforms (messaging providers, aggregators) that already operate an SMPP stack. ## What you can do - **Send SMS, one at a time** — `POST /sms` for triggered traffic (OTPs, alerts, receipts, notifications). - **Send SMS in bulk** — `POST /sms/bulk` takes a collection of messages in a single request. This is the endpoint for API-driven campaigns, marketing fan-outs and any high-volume dispatch. Customers including marketing agencies run entire campaigns on top of it. - **Receive delivery reports** (DLRs) as carriers report status back through the chain, pushed to your webhook in real time. - **Accept inbound messages** when the account has two-way enabled. - **Query message history** and per-message status — `GET /sms`, `GET /sms/{id}`. - **Run HLR / number lookups** — `POST /lookup`, `GET /lookup/{id}` and `GET /lookup` to check handset presence and routing before sending. - **Read account balance** — `GET /organization/account`. - **Read price profiles** — `GET /sms/price-profile/me/countries` and `GET /lookup/price-profile/me/countries` for per-country pricing. ## When to use this API Reach for A2P Messaging whenever your code needs to send — one message or many — without asking Instasent to manage the recipient audience. Typical workloads: - **User-triggered messages** — OTPs, password resets, receipts, shipping notifications. One message per event, delivered in seconds, via `POST /sms`. - **API-driven campaigns and bulk dispatch** — marketing sends, notification fan-outs, anything that fires many messages in one go. Use `POST /sms/bulk`. - **Wholesale SMS over SMPP** — aggregators, messaging platforms and resellers pushing their own traffic through Instasent. [SMPP](/a2p-messaging-api/smpp/integration) gives carrier-grade throughput over persistent sessions; HTTP also works at moderate rates. - **Pre-send validation and pricing** — HLR lookup and price profiles to route and cost-check before dispatch. If you need Instasent to own the audience, personalization, segmentation and attribution, use the [Product API](/platform-api/product-api/guide) instead — it delegates delivery to this one but adds the customer-data layer on top. The A2P Messaging API is the right pick when the caller already manages its recipient list and just needs to send. ## What to read next - [HTTP Quickstart](/a2p-messaging-api/http/quickstart) - Send your first SMS over REST in under five minutes. - [Bulk sending](/a2p-messaging-api/http/bulk) - Up to 100 messages per request, queue-based dispatch, the endpoint for API-driven campaigns. - [HTTP Authentication](/a2p-messaging-api/http/authentication) - Token types, header vs. query string, rotation. - [Receiving DLRs](/a2p-messaging-api/http/dlrs) - Webhook payload, status list, two-way inbound. - [Full API Reference](/a2p-messaging-api/http/reference) - Every endpoint, every parameter. --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/senders # Senders & coverage The `from` field is a sender, and some countries require it to be registered before an operator accepts it. This page covers what changes for an API integration: the Fallback Sender that carries unregistered traffic, the CNMC provider requirement, and what is billed. **Language:** en **Audience:** developer **TLDR:** Register the senders you send from — that is the short version. Traffic whose `from` matches no registered sender of the project travels under the project's Fallback Sender, which in some countries means best-effort delivery and in others means the message does not go out. Spain additionally requires your company to be a provider (PRO) registered with the CNMC, and their registry is the authority. Blocked transit messages are billed; blocked messages from a registered sender of your own are not. **Search keywords:** from field, sender id, unregistered sender, fallback sender, wildcard sender, wildcard route, in transit sender, transit traffic, sender registration api, cnmc provider registry, blocked and charged, numeric sender, alias spelling, exact alias, case sensitive sender, opt out substitution **Related pages:** /a2p-messaging-api/http/quickstart, /platform/en/channels/sms/coverage-and-delivery, /platform/en/channels/countries **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/senders/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/senders.md (Markdown) The `from` field you send is a **sender** — the name or number the recipient sees. It works the same through this API as it does in the dashboard, and the rules that govern it are the same ones: some countries accept any alphanumeric sender, some replace it with a number, and some require it to be **registered** before an operator will accept it as yours. Those per-country rules are documented once, for both surfaces: - [Coverage & delivery](/platform/en/channels/sms/coverage-and-delivery) - What happens when you send to a country that requires registration and yours isn't approved yet. - [Countries](/platform/en/channels/countries) - Every destination, with what it asks for on each channel. ## Register the senders you send from **That is the short version of this whole page.** Register each sender for the countries you send to, and your messages arrive under your brand, predictably, with nothing else to think about. On **RCS it isn't a recommendation but a requirement**: an agent has to be registered per country before anything is sent there. Everything below describes what happens when you haven't — which is worth knowing, but is not the state to operate in. ## Traffic with no registered sender In the dashboard you pick a sender you created. Through the API you put any string in `from`, and it may not correspond to any sender registered for your project. That traffic isn't rejected. It travels under the project's **Fallback Sender** — in full, the **Wildcard (In transit) Sender**, which is how you'll see it named in the dashboard and how the regulator refers to the traffic it carries. It is a sender with no alias of its own, and it exists precisely to carry whatever doesn't match a registered one. You don't create it: the project has at most one, and it is set up for you. **In some countries that means best-effort delivery**, along the lines set out in [Coverage & delivery](/platform/en/channels/sms/coverage-and-delivery): we look for the best available route so the message still arrives, without promising it will. In others there is no alternative route at all, and a message that the operator won't accept simply doesn't go out. Which of the two applies depends on the destination and on how your request is formed — and one condition matters more here than anywhere else, because it only exists for API traffic: see [A numeric `from` doesn't qualify](#a-numeric-from-doesnt-qualify). > **Note**: Your reports, exports, DLRs and webhooks always show the `from` **you sent**. Where the > sender is substituted, what changes is what reaches the handset — never what your > integration reads back. ## Spain: you have to be a PRO, registered as such with the CNMC This is the requirement most likely to catch an integration out, because it is met **outside Instasent** and nothing in your code will tell you it is missing. It is also the reason this page exists rather than a link: it applies to wholesale traffic, so the product documentation — written for end customers — doesn't cover it. To carry traffic into Spain under the Fallback Sender, your company has to be a **provider (PRO)** and be **registered as one with the CNMC**. We supply the identifier you filed with them — taken from your legal profile, or from your organisation's details — and the CNMC's own registry is what decides: if your company isn't listed and active there, the message doesn't go out. **The authority is their registry, not any status in our system.** A registration can look fine on our side and still be refused because the provider check fails. > **Warning**: If you route Spanish traffic through the API and your company isn't registered with > the CNMC as a provider, sort that out before the traffic matters. It is a filing with > the regulator, not a setting in the dashboard. ## Send the alias exactly as you filed it Where a sender is filed with a regulator, the check against that registry is **literal** — character by character, capitals included. Nothing is normalised on the way. If you filed **`PEDRO`** and your request carries **`Pedro`**, that check reads your alias as not filed, with all the consequences of not being filed — even though the procedure went through and was approved. It is worth a line in your integration, because it is the hardest failure to diagnose from the outside: the register shows as approved, the regulator has your alias, and the messages still don't arrive as they should. If you build `from` from a database field or a template, make sure it reproduces the filed spelling. ## A numeric `from` doesn't qualify The Fallback Sender has no alias of its own — the alias travels in the message, in your `from`. And that is what is examined when a message needs an alternative route. **A `from` made of digits never takes an alternative route.** Where an alphanumeric `from` would be carried best-effort, a numeric one is not. This only affects API traffic, because in the dashboard the sender is always one you created. That splits into two very different cases, and the difference is whether what you sent is a real phone number: - **A valid international phone number** doesn't need an alternative route in Spain: it is **exempt from the CNMC** and accepted without any filing. - **A string of digits that isn't a phone number** — `123`, an internal code, a short reference — is neither exempt nor carried: it has no route and no exemption, so it doesn't go out. If you send numeric senders, send real phone numbers. ## Choosing blocking over substitution Some senders would rather not go out at all than go out with the recipient seeing a different sender — a brand where the name is the point, or traffic where an unexpected sender would raise a support case of its own. The sender carries a field for that: **`unregisteredBypassRouteOptOut`**. With it on, in a country whose regulator is in force, that sender never takes an alternative route — the block applies as it stands, and the message is reported as blocked by regulation. Two limits worth knowing before you set it: - **It changes nothing where there is no regulator**, and nothing before a regulator's rules take effect. It isn't a global "never substitute my sender" switch. - **It doesn't change what is billed.** Transit traffic blocked this way is billed just the same; a specific sender of your own, blocked, is not — the same asymmetry as in [What is billed](#what-is-billed). It lives on the sender, not on the account, on purpose: a Fallback Sender covers all of a reseller's unregistered traffic, so the choice belongs with the sender that carries it. ## What is billed **A blocked transit message is billed. A blocked message sent from a registered sender of your own is not.** That asymmetry is deliberate, and it is the most concrete reason to register the countries you send to: traffic riding the Fallback Sender costs you the same whether it arrives or not. Delivery reports tell you the outcome per message — see [DLRs](/a2p-messaging-api/http/dlrs). ## RCS doesn't work this way There is no equivalent on RCS, and there won't be. An RCS agent is always tied to a known provider, so there is no sender without an alias for traffic to travel under: the agent has to be registered for each country before anything is sent there. --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/quickstart # A2P Messaging API quickstart Send your first SMS over the A2P Messaging API in under five minutes. Grab a token, fire one HTTP request, and point a webhook at the DLR URL to see delivery. **Language:** en **Audience:** developer **TLDR:** Send an SMS with POST https://api.instasent.com/transactional/v1/sms, header Authorization: Bearer , and a JSON body containing from, to, and text. The response returns an id; point your DLR webhook at it to track delivery. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/quickstart/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/quickstart.md (Markdown) The A2P Messaging API over HTTP needs three things: an account, an `api_sms` token and one POST request. This walkthrough takes you from zero to a delivered message. If you already know you need high throughput or API-driven campaigns, jump straight to [Bulk sending](/a2p-messaging-api/http/bulk) — same token, one call, up to 100 messages per request. #### 1. Create an Instasent account Sign up at [instasent.com](https://instasent.com) and complete onboarding. #### 2. Create a token You create the token yourself. In the dashboard, open your A2P Messaging API project, select **API tokens** in the sidebar and click **Create API token**. The same action is the first step, **Create a token**, of the **Get your API project ready** checklist on the project home. Treat the token as a secret: never commit it to version control. #### 3. Send your first message #### curl ```bash curl -X POST https://api.instasent.com/transactional/v1/sms \ -H "Authorization: Bearer $INSTASENT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "from": "Instasent", "to": "+34600000000", "text": "Hello from Instasent" }' ``` #### node ```js const res = await fetch("https://api.instasent.com/transactional/v1/sms", { method: "POST", headers: { Authorization: `Bearer ${process.env.INSTASENT_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ from: "Instasent", to: "+34600000000", text: "Hello from Instasent", }), }); const sms = await res.json(); ``` #### python ```python import os, requests res = requests.post( "https://api.instasent.com/transactional/v1/sms", headers={"Authorization": f"Bearer {os.environ['INSTASENT_TOKEN']}"}, json={ "from": "Instasent", "to": "+34600000000", "text": "Hello from Instasent", }, ) sms = res.json() ``` #### 4. Check delivery The response contains an `id`. Point your webhook endpoint at the DLR URL in your project settings — Instasent will `POST` delivery updates as the message progresses through the carrier network. See [Receiving DLRs](/a2p-messaging-api/http/dlrs) for payload details. ## Sending many messages at once For API-driven campaigns, notification fan-outs or any high-volume dispatch, switch from `POST /sms` to `POST /sms/bulk` — a single call that carries up to 100 messages, returns accepted and rejected items side by side, and queues the accepted ones on the platform for fastest-possible dispatch. The same `api_sms` token works. ```bash curl -X POST https://api.instasent.com/transactional/v1/sms/bulk \ -H "Authorization: Bearer $INSTASENT_TOKEN" \ -H "Content-Type: application/json" \ -d '[ { "from": "Instasent", "to": "+34600000001", "text": "Hello one" }, { "from": "Instasent", "to": "+34600000002", "text": "Hello two" } ]' ``` Full request and response shape, per-request limits, throughput model and partial-success handling live in [Bulk sending](/a2p-messaging-api/http/bulk). > **Note**: **About that `from`.** It is a sender, and some countries require it to be registered > before an operator accepts it as yours — Spain also requires your company to be listed > in the CNMC's provider registry. Sending without that doesn't fail loudly: the message > goes out best-effort and is billed either way. Before you route real traffic into a > regulated market, read > [Senders and country coverage](/a2p-messaging-api/senders). ## What's next - **[Senders and country coverage](/a2p-messaging-api/senders)** — which countries require registering the `from`, what carries traffic that matches no registered sender, and what is billed when a message is blocked. - **[Bulk sending](/a2p-messaging-api/http/bulk)** — `POST /sms/bulk`, 100 messages per request, queue-based throughput. The endpoint to use for campaigns. - **[Authentication](/a2p-messaging-api/http/authentication)** — token scopes, rotation, header vs. query-string. - **[Rate limits](/a2p-messaging-api/http/rate-limits)** — per-endpoint limits and how to read the `X-RateLimit-*` headers. One bulk call counts as one request against the window. - **[Errors](/a2p-messaging-api/http/errors)** — status codes, error payload shape, retry guidance. - **[Receiving DLRs](/a2p-messaging-api/http/dlrs)** — webhook payload and status list. - **[Reference](/a2p-messaging-api/http/reference)** — every endpoint, every parameter. --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/bulk # Bulk sending Send many SMS in a single request via POST /sms/bulk — the A2P Messaging API's throughput endpoint for campaigns, notification fan-outs and high-volume dispatch. Covers the request and response shape, per-request limits, the queue-based throughput model, partial-success handling and when to prefer SMPP. **Language:** en **Audience:** developer **TLDR:** POST /sms/bulk takes a top-level JSON array of 1 to 100 messages with the same fields as POST /sms, and returns 201 with entity (the accepted messages, each with its id) and errors (rejected items, with per-field messages): a 201 does not mean every item was accepted. Accepted messages are queued and dispatched at carrier speed, each with its own DLRs; one bulk call counts as one request against the rate limit, and more than 100 items returns 413. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/bulk/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/bulk.md (Markdown) The maximum throughput you can get out of the A2P Messaging API over HTTP is through bulk. One `POST /sms/bulk` request carries up to 100 messages; Instasent validates each item, queues the accepted ones on the platform and dispatches them to the carrier network as fast as the routes allow. From the caller's point of view the HTTP request returns as soon as the batch is staged — the delivery loop is ours to run. That is why an API-driven campaign sent through bulk will out-perform the same campaign fired as thousands of individual `POST /sms` calls, regardless of how much concurrency the caller puts on the client side. This page covers the request and response shape, the per-request limits, how the queue behaves under load, DLRs, partial-success handling, and when to reach for SMPP instead. ## How a bulk request flows ``` Your backend ──POST /sms/bulk──▶ Instasent API ──validate──▶ queue ──▶ carrier ──▶ handset ◀──── 201 {entity, errors} ──── │ ▼ Your webhook ◀────────────────────── DLR per message ──────────────────────────────── carrier ``` - **Submit** a single `POST` with an array of up to 100 SMS items. - **Validate**: the server checks each item independently. Valid ones are accepted; invalid ones are rejected with field-level errors, and the rest of the batch still goes through. - **Queue**: accepted messages are staged on the platform's dispatch queue. The HTTP request returns immediately with the list of accepted messages under `entity` (each with an `id`) and the list of `errors` (rejected, with per-field messages). It does **not** block on carrier delivery. - **Dispatch**: the platform drains the queue concurrently against the carrier routes. Throughput is bounded by route capacity, not by your HTTP client. - **DLR**: each accepted message produces its own DLR webhook events, exactly like a single send. Match them by `id`. The practical consequence for campaigns: submit as many full batches of 100 as you can afford on your rate-limit window; the queue absorbs the burst and drains at carrier speed. Trying to match that throughput through `POST /sms` one at a time will hit the rate-limit wall long before the queue does. ## Request Send a `POST` to `/sms/bulk` with an `api_sms` bearer token and a JSON **array** of items as the body — there is no wrapper object. #### curl ```bash curl -X POST https://api.instasent.com/transactional/v1/sms/bulk \ -H "Authorization: Bearer $INSTASENT_TOKEN" \ -H "Content-Type: application/json" \ -d '[ { "from": "Instasent", "to": "+34600000001", "text": "Hello one" }, { "from": "Instasent", "to": "+34600000002", "text": "Hello two" }, { "from": "Instasent", "to": "+34600000003", "text": "Hello three" } ]' ``` #### node ```js const messages = [ { from: "Instasent", to: "+34600000001", text: "Hello one" }, { from: "Instasent", to: "+34600000002", text: "Hello two" }, { from: "Instasent", to: "+34600000003", text: "Hello three" }, ]; const res = await fetch("https://api.instasent.com/transactional/v1/sms/bulk", { method: "POST", headers: { Authorization: `Bearer ${process.env.INSTASENT_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(messages), }); const { entity, errors } = await res.json(); ``` #### python ```python import os, requests messages = [ {"from": "Instasent", "to": "+34600000001", "text": "Hello one"}, {"from": "Instasent", "to": "+34600000002", "text": "Hello two"}, {"from": "Instasent", "to": "+34600000003", "text": "Hello three"}, ] res = requests.post( "https://api.instasent.com/transactional/v1/sms/bulk", headers={"Authorization": f"Bearer {os.environ['INSTASENT_TOKEN']}"}, json=messages, ) data = res.json() ``` Each item accepts the same fields as `POST /sms`: | Field | Required | Notes | | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `from` | yes | Sender ID. 3–14 characters — up to 11 alphanumeric chars or up to 14 digits. | | `to` | yes | Destination MSISDN in E.164 format (e.g. `+34600000000`). | | `text` | yes | Message body. | | `allowUnicode` | no | Defaults to `false`. Set to `true` if the text may contain non-GSM-7 characters. | | `clientId` | no | Your own reference (≤40 chars). Must be unique per SMS. Echoed back on the response and on DLRs — use it to correlate without storing Instasent's `id`. | ## Response `201 Created` with a JSON object containing two arrays: ```json { "entity": [ { "id": "...", "clientId": null, "from": "Instasent", "to": "+34600000001", "text": "Hello one", "status": "...", "..." : "..." } ], "errors": [ { "fields": { "to": ["This value is not valid"] } } ] } ``` - `entity`: one object per **accepted** message, with its own Instasent `id`. The key is singular but always holds an array. Track that `id` (or the `clientId` you sent) against the DLRs you will receive. - `errors`: one object per **rejected** item, with per-field error messages under `fields`. The order mirrors the input array position of the rejected items. A 201 means the batch was processed — not that every item was accepted. Always inspect `errors` before assuming the whole batch went through. ## Limits per request | Limit | Value | What happens when you exceed it | | ---------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Items per request | **1 – 100** | `413 Request Entity Too Large`. Split the batch and retry. | | Per-item validation | See field table above | The invalid item lands in `errors`; the rest of the batch still goes through. | | Per-account rate limit | See [Rate limits](/a2p-messaging-api/http/rate-limits) | `429 Too Many Requests`. One bulk call counts as **one** request against the window — that is what makes bulk so much more rate-limit-efficient than looping over `POST /sms`. | If you need to dispatch more than 100 messages, split the payload across multiple bulk requests and fire them concurrently. Throughput scales linearly with the number of parallel batches until you reach your account's rate limit. > **Tip**: Batching only makes the HTTP side efficient. The dispatch queue is shared across all your traffic — so once your batches are in the queue, there is no ordering guarantee between them. If two messages in the same batch must go out in a fixed order, send them as separate calls. ## Throughput, queueing and back-pressure The dispatch queue is sized to absorb large campaigns. In practice this means: - **Bursts are absorbed**. A sudden wave of bulk submissions does not translate into rejections downstream — messages wait in the queue and drain as the carrier routes accept them. - **Throughput is carrier-bound**, not HTTP-bound. The limiting factor is the route capacity and the destination country, not your HTTP client's concurrency. - **Higher ceilings are a conversation**. If the default rate limit gets in the way of a campaign's peak, open a ticket with the expected volume and we will raise the window on your account. > **Tip**: For sustained carrier-grade throughput (aggregators, messaging platforms, resellers), consider [SMPP](/a2p-messaging-api/smpp/integration) instead. Same underlying pipeline, persistent TCP sessions, binary protocol — no HTTP overhead per message. ## Delivery reports DLRs work the same as for a single send: one webhook POST per status transition, keyed by the message `id`. See [Receiving DLRs](/a2p-messaging-api/http/dlrs) for the payload shape, the status list and how the same message can produce multiple DLRs during its lifecycle. If you sent a `clientId`, it is echoed on every DLR for that message — useful when you would rather not store Instasent's `id` on your side. ## Handling partial failures A pragmatic pattern in your caller: #### 1. Build the batch with your own correlation id Include `clientId` on every item so you can match responses and DLRs back to records on your side without an extra lookup. #### 2. Fire the bulk request One HTTP call per batch of up to 100 messages. Check the HTTP status — `201` is the happy path. #### 3. Reconcile entity and errors Iterate both arrays. For each item in `entity`, record the returned `id` (or your `clientId`) and mark the message as accepted. For each item in `errors`, log the per-field message and retry only if it is a transient issue. #### 4. Process DLRs asynchronously Delivery status arrives on the webhook, not on the HTTP response. Update the final state as DLRs come in. ## HTTP bulk vs SMPP | | HTTP `/sms/bulk` | SMPP | | -------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------- | | Setup cost | `api_sms` token + one HTTP call | Persistent TCP session, binary protocol, more ops overhead | | Best for | API-driven campaigns, notification fan-outs, periodic dispatch, embedded use cases | Aggregators, messaging platforms, carrier-grade sustained throughput | | Per-message overhead | HTTP request / JSON | Binary PDU on an open session | | Throughput ceiling | Bounded by rate limit on your account; excellent for bursts | Highest — designed for continuous streaming | | Engineering lift | Low | Higher (session management, retries, pluggable TLVs) | Start on HTTP bulk. Move to SMPP only when the sustained traffic profile — not a one-off burst — justifies the extra engineering lift. ## Pitfalls - **Wrapping the body in a `messages` object.** The body is a JSON array at the top level. Wrapping it breaks validation. - **Treating 201 as "all accepted".** Always iterate `errors` — per-item rejections live there, not in the HTTP status. - **Parallel `POST /sms` instead of bulk.** You will burn the rate-limit window without raising the ceiling. The queue is what gives bulk its throughput, not HTTP concurrency. - **Forgetting `clientId`.** Without it you have to store Instasent's `id` to correlate DLRs — an avoidable dependency. - **Batching items that need strict ordering.** The queue does not preserve submission order across the platform. If ordering matters between two messages, send them separately. ## What's next - **[Receiving DLRs](/a2p-messaging-api/http/dlrs)** — webhook payload, status list, per-message matching. - **[Rate limits](/a2p-messaging-api/http/rate-limits)** — how the window is counted and how to ask for more. - **[Errors](/a2p-messaging-api/http/errors)** — status codes and retry guidance, including `413` and `422`. - **[SMPP integration](/a2p-messaging-api/smpp/integration)** — the next step up when HTTP is not enough. - **[API Reference](/a2p-messaging-api/http/reference)** — full shape of `POST /sms/bulk` and every other endpoint. --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/authentication # A2P Messaging API authentication The A2P Messaging API authenticates each request with a bearer token. Create it in the dashboard, send it in the Authorization header, and rotate it when it leaks. **Language:** en **Audience:** developer **TLDR:** Create an api_sms token for the project in the dashboard under API tokens (it is shown once) and send it as Authorization: Bearer ; the access_token query-string parameter also works, but only for quick tests from trusted shells. Tokens don't expire: to rotate one, create the replacement first, redeploy, then delete the old token, and any request still using it gets 401 Unauthorized. **Search keywords:** api key, api token **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/authentication/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/authentication.md (Markdown) Every request to the A2P Messaging API carries a token. Tokens are issued in the dashboard, scoped per project, and passed either in the `Authorization` header (recommended) or as a query-string parameter (convenient for quick tests). ## Getting a token Sign in to the [Instasent dashboard](https://dashboard.instasent.com), open **API tokens** and create an `api_sms` token for the project that will send the traffic. Copy the value straight away — it is shown once. > **Warning**: Treat tokens as production secrets. Keep them in an environment variable or a secrets manager; never commit them to the repo or embed them in client-side code. ## Sending the token ### In the `Authorization` header Preferred in every environment. The token never appears in URLs, logs or the browser history. ```bash curl https://api.instasent.com/transactional/v1/sms \ -H "Authorization: Bearer $INSTASENT_TOKEN" ``` ### As a query-string parameter Convenient for one-off checks from a browser or a copy-pasted curl. Only use it from trusted shells — URLs are logged by proxies and CDNs. ```bash curl "https://api.instasent.com/transactional/v1/sms?access_token=$INSTASENT_TOKEN" ``` ## Rotating a token Tokens do not expire. Rotate them whenever a member of the team leaves, whenever a secret might have been exposed, and at least once a year as a hygiene measure. #### 1. Issue the replacement Create a new `api_sms` token in the dashboard **before** revoking the old one. This keeps traffic flowing while you redeploy. #### 2. Roll the new token out Update your secrets store and redeploy the workers that call the API. #### 3. Revoke the old token Once the replacement is live everywhere, delete the old token in the dashboard. Any request still using it will fail with `401 Unauthorized`. ## What's next - **[Rate limits](/a2p-messaging-api/http/rate-limits)** — how many requests per minute and what the `X-RateLimit-*` headers tell you. - **[Errors](/a2p-messaging-api/http/errors)** — status codes returned by the API and how to retry safely. --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/rate-limits # A2P Messaging API rate limits A2P Messaging HTTP traffic is rate-limited per account and per endpoint. Each response reports the current window through X-RateLimit headers, and breaching the limit returns 429. **Language:** en **Audience:** developer **TLDR:** Limits are counted per account and per endpoint over a 60-second window, shared by all the account's tokens: 1200 requests for POST /sms, POST /sms/bulk (one call is one request, with its own counter) and most endpoints, but only 10 for GET /sms and 20 for GET /sms/{id}, so track delivery with DLR webhooks instead of polling. Going over returns 429 with a plain-text body; retry after X-RateLimit-Reset. For a higher ceiling, open a ticket. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/rate-limits/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/rate-limits.md (Markdown) Every A2P Messaging endpoint enforces a ceiling of requests per 60-second window. The counter is kept per account and per endpoint: all the tokens of an account share one counter for each endpoint, so spreading traffic across several tokens does not add capacity. If you need more throughput on a specific endpoint, open a ticket with the expected peak rate and we will raise the ceiling on your account. ## Limits per endpoint | Endpoint | Requests per 60 seconds | | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `POST /sms` | 1200 | | `POST /sms/bulk` | 1200. One call counts as one request, however many messages it carries, and it has its own counter, separate from `POST /sms`. | | `GET /sms` | 10 | | `GET /sms/{id}` | 20 | | `POST /lookup`, `GET /lookup`, `GET /lookup/{id}` | 1200 each | | `GET /organization/account` | 1200 | | `GET /sms/price-profile/me/countries`, `GET /lookup/price-profile/me/countries` | 1200 each | With so few reads allowed on `GET /sms` and `GET /sms/{id}`, track delivery through [DLR webhooks](/a2p-messaging-api/http/dlrs) instead of polling. ## Reading the headers Every response includes three headers with the current window state. Log them in production — they are the cheapest way to spot a client that is about to hit the wall. ``` X-RateLimit-Limit 1200 X-RateLimit-Remaining 1195 X-RateLimit-Reset 1893452400 ``` | Header | Meaning | | ----------------------- | --------------------------------------------- | | `X-RateLimit-Limit` | Total requests allowed in the current window. | | `X-RateLimit-Remaining` | Requests left before the window tightens. | | `X-RateLimit-Reset` | Unix timestamp when the counter resets. | ## When you hit the limit Requests that exceed the window return **`429 Too Many Requests`**. The body is the plain text `You exceeded the rate limit` (not JSON); the `X-RateLimit-Reset` header tells you when to retry. > **Tip**: Back off exponentially rather than retrying in a tight loop. A client that hammers a 429 response keeps the window full and never recovers — waiting until `X-RateLimit-Reset` resolves the situation cleanly. ## What's next - **[Errors](/a2p-messaging-api/http/errors)** — every status code you might receive, including `429`. - **[API Reference](/a2p-messaging-api/http/reference)** — per-endpoint documentation. --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/errors # A2P Messaging API errors The A2P Messaging HTTP API uses standard HTTP status codes. This page lists the ones you will encounter, what they mean and whether a retry is safe. **Language:** en **Audience:** developer **TLDR:** Branch on the HTTP status, not on the message text: most error bodies are JSON with error.message and error.code, but the 429 body is plain text. 400, 401, 402 (out of balance), 403, 404 and 413 mean the request or the account needs fixing and retrying won't help; retry only 429 and 5xx, with exponential backoff starting at 1 second. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/errors/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/errors.md (Markdown) Every response carries a meaningful HTTP status code. A `2xx` means the request was accepted; anything else signals a problem your code should handle explicitly. Most error bodies are JSON with an `error` object that carries a human-readable `message` and the numeric `code`; the exception is `429`, whose body is plain text. Branch your code on the HTTP status, not on the `message` text, which is written for people and can change. ## Status codes ### `400 Bad Request` The request body is malformed or has the wrong shape. Not retryable — fix the payload first. ```http HTTP/1.1 400 Bad Request ``` When the body is not valid JSON: ```json { "error": { "message": "Invalid JSON", "code": 400 } } ``` When the body of `POST /sms/bulk` is not a JSON array of objects (for example, a single object): ```json { "error": { "message": "Body should be a JSON array of objects", "code": 400 } } ``` ### `401 Unauthorized` The token is missing or does not exist (mistyped, rotated or deleted), or the request comes from an IP address outside the token's accepted IPs. Not retryable as is: check the token and the network you call from, and see the [Authentication](/a2p-messaging-api/http/authentication) guide. ```http HTTP/1.1 401 Unauthorized ``` ```json { "error": { "message": "No token could be found.", "code": 401 } } ``` ### `402 Payment Required` The account has run out of balance. Not retryable until funds are topped up in the dashboard. ```http HTTP/1.1 402 Payment Required ``` ### `403 Forbidden` The token exists but cannot be used for this request: it is disabled, the user or account is not enabled or is pending verification, or it is not an A2P Messaging API token (`api_sms`) with HTTP access. `GET /sms/{id}` also returns `403` when the message belongs to another account. Not retryable until the cause is fixed. The `message` differs from one cause to another, so branch on the `403` status rather than parsing the text. ```http HTTP/1.1 403 Forbidden ``` ### `404 Not Found` The resource (message id, token id, project) does not exist or is not visible to the current token. ```http HTTP/1.1 404 Not Found ``` ### `413 Request Too Large` The request body exceeds the maximum accepted size. Not retryable — trim the payload. ```http HTTP/1.1 413 Request Too Large ``` ### `429 Too Many Requests` You hit the per-endpoint rate limit. The body is the plain text `You exceeded the rate limit`, not JSON, so do not try to parse it. The `X-RateLimit-Reset` header says when to retry. See [Rate limits](/a2p-messaging-api/http/rate-limits). ```http HTTP/1.1 429 Too Many Requests You exceeded the rate limit ``` ### `500 Internal Server Error` Something broke on our side. Retry with exponential backoff; if the failure persists, contact support with the request id. ```http HTTP/1.1 500 Internal Server Error ``` ## Retry policy > **Tip**: Retry `429` and `5xx` with exponential backoff, starting at 1 s and capping at a minute or so. Everything in the `4xx` range other than `429` means the request itself is wrong — retrying will not help. ## What's next - **[Receiving DLRs](/a2p-messaging-api/http/dlrs)** — delivery reporting for messages that did leave the API successfully. - **[API Reference](/a2p-messaging-api/http/reference)** — per-endpoint responses. --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/dlrs # Receiving DLRs Delivery reports (DLRs) are POSTed to a webhook URL configured per API token. Each call describes one status transition — sent, delivered, failed, inbound — with a machine-readable status and code. **Language:** en **Audience:** developer **TLDR:** Delivery reports are POSTed as JSON (id, clientId, status, code, eventAt) to the webhook configured per api_sms token; with two-way enabled, inbound messages arrive on the same endpoint with a message field. Reply 2xx in under 5 seconds, since anything else is retried with exponential backoff. One message can produce several DLRs (sent, buffered, delivered…), so key on id and keep the latest status. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/dlrs/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/http/dlrs.md (Markdown) A DLR (Delivery Receipt) is how carriers tell you what happened to a message after it left Instasent. We aggregate the updates from every hop in the chain and forward each transition to a webhook you control. If two-way messaging is enabled on your account, inbound messages arrive on the same endpoint. ## Configuring the webhook Webhooks are configured per `api_sms` token in the dashboard. Point it at a public HTTPS endpoint that returns `2xx` quickly — we consider any non-2xx a failure and retry with exponential backoff. > **Warning**: Respond in under 5 seconds with a `2xx` and process the payload asynchronously. Long-running handlers trigger timeouts, retries and duplicate deliveries. ## Payload shape Every call is a `POST` with a JSON body: ```json { "id": "sms-id", "clientId": "custom-id", "status": "delivered", "code": 0, "eventAt": "2026-04-21T10:15:00Z" } ``` | Field | Description | | ---------- | ---------------------------------------------------------------------------------------------------------------- | | `id` | The Instasent message id. Matches the `id` returned when the SMS was created. | | `clientId` | The `clientId` you supplied when creating the message, if any — useful for correlation against your own records. | | `status` | Machine-readable status. See the list below. | | `code` | Numeric code that narrows down the reason for `error`/`failed` statuses. | | `eventAt` | ISO-8601 timestamp of the event. | When two-way is enabled on the account, the payload also carries a `message` field with the inbound text. ## Status list | Status | Meaning | | ----------- | ------------------------------------------------------------ | | `sent` | Message was sent to the device. | | `accepted` | Message was accepted by the carrier. | | `buffered` | Message is buffered by the carrier, waiting to be delivered. | | `delivered` | Message was delivered to the handset. | | `error` | Message could not be sent to the device. | | `failed` | Message could not be delivered. | | `expired` | Message expired before delivery was possible. | | `canceled` | Message was canceled. | | `rejected` | Message was rejected by the carrier. | | `unknown` | An unknown error occurred. | | `stop` | An opt-out inbound message was received from the handset. | | `inbound` | An inbound message was received (two-way only). | ## Code list Codes narrow down the reason for non-delivery. `0` is the happy path; everything else describes a specific failure class. | Code | Meaning | | ----- | ------------------ | | `0` | OK | | `1` | Unknown | | `2` | Absent temporarily | | `3` | Absent permanently | | `4` | Blocked subscriber | | `5` | Portability error | | `6` | Antispam reject | | `7` | Line busy | | `8` | Network error | | `9` | Illegal number | | `10` | Invalid message | | `11` | Unroutable | | `12` | Unreachable | | `13` | Age restriction | | `14` | Blocked carrier | | `15` | Insufficient funds | | `16` | Flooded | | `99` | Unknown error | | `100` | Reject | ## Idempotency The same message may generate multiple DLRs — typically `sent` → `buffered` → `delivered`. Use `id` as the primary key and treat each DLR as a status transition, not as the final state. > **Tip**: Persist the latest `status` and `eventAt` per `id` rather than appending every event. If your pipeline needs the full audit trail, log the raw payload to a separate store. ## What's next - **[Errors](/a2p-messaging-api/http/errors)** — for failures that happen before the message even leaves the API. - **[API Reference](/a2p-messaging-api/http/reference)** — retrieve the current status of a message on demand. --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/smpp/authentication # SMPP authentication The A2P Messaging API over SMPP authenticates each session with a system_id and password carried on the bind PDU. Credentials are issued in the dashboard once SMPP is enabled on the account. **Language:** en **Audience:** developer **TLDR:** SMPP is enabled per organization on request (open a ticket); each api_sms token then shows a system_id and password pair, and its HTTP token keeps working in parallel. Bind once to smpp.instasent.com port 2775 (SMPP v3.4 or v5.0) with bind_transceiver, bind_transmitter or bind_receiver, keep the session open and send enquire_link periodically. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/smpp/authentication/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/smpp/authentication.md (Markdown) SMPP authentication is per-session, not per-request. The client binds once, authenticates with `system_id` + `password`, and keeps the TCP connection open for the rest of the traffic window. ## Enabling SMPP on your account Open a ticket to have SMPP enabled on your organization. Once it's on, every `api_sms` token in the dashboard exposes an extra pair of credentials — `systemID` and `password` — for use over SMPP. The HTTP token remains usable in parallel. ## Server endpoint | Field | Value | | ----------------- | -------------------------------------------------------------------------------------------------- | | Host | `smpp.instasent.com` | | Port | `2775` | | Protocol versions | [SMPP v3.4](https://smpp.org/SMPP_v3_4_Issue1_2.pdf) and [SMPP v5.0](https://smpp.org/SMPP_v5.pdf) | ## Binding Use `bind_transceiver`, `bind_transmitter` or `bind_receiver` depending on the direction of traffic you need (see [Integration](/a2p-messaging-api/smpp/integration)). The required fields are the same on all three. | Field | Description | | ----------- | ----------------------------------------------------------- | | `system_id` | SMPP username, as shown next to the token in the dashboard. | | `password` | SMPP password, paired with the `system_id`. | Example PDU: ```json { "command": "bind_transceiver", "command_id": 9, "system_id": "myuser", "password": "mypass" } ``` > **Tip**: Keep sessions long-lived and issue `enquire_link` PDUs periodically to avoid idle disconnects from intermediate firewalls. Re-binding on every message defeats the point of SMPP. ## What's next - **[Integration](/a2p-messaging-api/smpp/integration)** — PDUs supported, sending, concatenation, DLRs, inbound messages. --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/smpp/integration # Integration Supported PDUs, submit_sm fields, message concatenation, DLRs over deliver_sm and inbound handling for the A2P Messaging API over SMPP. **Language:** en **Audience:** developer **TLDR:** Send with submit_sm: source_addr of 3 to 11 characters, destination_addr in E.164, registered_delivery=1 to receive DLRs, and data_coding 0 (GSM7), 3 (Latin-9) or 8 (UCS2). For long texts use message_payload or UDHI segmentation (esm_class=0x40). DLRs arrive as deliver_sm with esm_class=4 in the usual id/stat/err format, and inbound messages as deliver_sm with esm_class=0. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/smpp/integration/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/smpp/integration.md (Markdown) Once the session is bound (see [Authentication](/a2p-messaging-api/smpp/authentication)), traffic flows through a small set of PDUs. This page documents what is supported and how the payload is shaped. ## Supported PDUs | PDU | Purpose | | ------------------ | ----------------------------------------- | | `bind_transceiver` | Send and receive SMS on the same session. | | `bind_transmitter` | Send SMS only. | | `bind_receiver` | Receive SMS only. | | `submit_sm` | Send a mobile-terminated (MT) message. | | `deliver_sm` | Receive DLRs and inbound messages. | | `enquire_link` | Keep the session alive. | | `unbind` | Log out — SMPP v5.0 only. | ## Sending messages Use `submit_sm` to send an MT. The fields below are recognised; everything else is ignored or rejected depending on the PDU version. | Field | Description | Notes | | --------------------- | ------------------------------------------------- | ------------------------------------------ | | `source_addr` | Sender (from). | 3–11 chars. | | `destination_addr` | Recipient MSISDN. | E.164 with country prefix. | | `esm_class` | Message mode and type. | See concatenation below. | | `registered_delivery` | Whether to request DLRs. | `1` to receive DLRs. | | `data_coding` | Character encoding. | See table below. | | `short_message` | Message body. | Up to the encoding-dependent length limit. | | `message_payload` | Alternative to `short_message` for long messages. | See concatenation. | Example PDU: ```json { "command": "submit_sm", "command_id": 4, "source_addr": "INSTASENT", "destination_addr": "+34676388300", "esm_class": 0, "registered_delivery": 1, "short_message": "example of message" } ``` ### Data coding | Value | Encoding | | ----- | -------------------- | | `0` | GSM7 | | `3` | ISO-8859-15 (LATIN9) | | `8` | UTF-16BE (UCS2) | ## Message concatenation Messages that exceed the single-segment limit can be delivered in two ways. Pick whichever fits your SMPP client. ### `message_payload` Put the whole text in `message_payload` instead of `short_message`. The server segments for you. ```json { "command": "submit_sm", "source_addr": "INSTASENT", "destination_addr": "+34666000000", "esm_class": 0, "registered_delivery": 1, "message_payload": "Y, viéndole don Quijote de aquella manera, con muestras de tanta tristeza, le dijo: Sábete, Sancho, que no es un hombre más que otro si no hace más que otro." } ``` ### UDHI segmentation Set the User Data Header Indicator on `esm_class` and build the concatenation header yourself. ``` esm_class = 0x40 ``` The UDH prefix is inserted at the start of the message body: | Byte | Description | | ---- | ------------------------------------------------------------------- | | `05` | Length of UDH (5 bytes). | | `00` | Indicator for concatenated message. | | `03` | Subheader length (3 bytes). | | `XX` | Message identifier — any hex value, must match across all segments. | | `YY` | Total number of segments. | | `ZZ` | Sequence number of this segment. | Two-segment example: ``` # segment 1 esm_class = 0x40 short_message = 0x05 0x00 0x03 0x05 0x02 0x01 Y, viéndole don Quijote de aquella manera, con muestras de tanta tristeza, le dijo: Sábete, Sancho, que no es un hombre más que # segment 2 esm_class = 0x40 short_message = 0x05 0x00 0x03 0x05 0x02 0x02 otro. Todas estas borrascas que nos suceden son señales de que presto ha de serenar el tiempo y han de sucedernos bien las cosas ``` ## Receiving DLRs DLRs come in through `deliver_sm` PDUs with `esm_class=4`. The body is a single line in the usual SMPP DLR format: ``` id:288230378411154593 sub:001 dlvrd:001 submit date:1602260445 done date:1602260345 stat:DELIVRD err:000 text:none ``` | Field | Description | | ------------- | --------------------------------- | | `id` | Instasent message id. | | `sub` | Unused. | | `dlvrd` | `1` delivered, `0` not delivered. | | `submit date` | Send date, `YYMMDDHHMM`. | | `done date` | Completion date, `YYMMDDHHMM`. | | `stat` | Status — see table below. | | `err` | Error code. | ### DLR statuses | Status | Description | | --------- | --------------------------------- | | `DELIVRD` | Message delivered to destination. | | `EXPIRED` | Message validity period expired. | | `DELETED` | Message deleted. | | `ACCEPTD` | Message accepted. | | `UNDELIV` | Message undeliverable. | | `UNKNOWN` | Message in an invalid state. | | `REJECTD` | Message rejected. | ## Receiving inbound messages Inbound (MO) messages arrive through `deliver_sm` with `esm_class=0`. The content is carried on `short_message.message`. --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/node # Node.js SDK Official Node.js client for the Instasent A2P Messaging API. Install via npm, instantiate the SMS client with your api_sms token, and call the high-level methods instead of wiring fetch by hand. **Language:** en **Audience:** developer **TLDR:** Install with npm install instasent, create an Instasent.SmsClient with your api_sms token and call sendSms(sender, to, text); getSms and getSmsById read messages. The SDK's canonical source is under review, so treat these method names as indicative and check the GitHub repository before production; anything else is a plain HTTP call against the API reference. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/node/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/node.md (Markdown) The Node.js SDK wraps the A2P Messaging HTTP endpoints so you do not have to build requests manually. If the SDK does not cover a call you need, drop back to plain `fetch` — the [Reference](/a2p-messaging-api/http/reference) describes every endpoint. > **Warning**: The canonical source for this SDK is under review. Until it is confirmed, treat the method names below as indicative and double-check against the [GitHub repository](https://github.com/instasent) before wiring it into production. ## Installation ```bash npm install instasent ``` ## Send an SMS ```js const Instasent = require("instasent"); const client = new Instasent.SmsClient(process.env.INSTASENT_TOKEN); const response = await client.sendSms("My company", "+34666666666", "test message"); console.log(response.response_code); console.log(response.response_body); ``` ## Available methods ```js client.sendSms(sender, to, text); client.getSms(page, perPage); client.getSmsById(messageId); ``` ## Getting help For help installing or using the library, contact [support@instasent.com](mailto:support@instasent.com). Bugs and feature requests belong on the library's GitHub repository. --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/php # PHP SDK Official PHP client for the Instasent A2P Messaging API. Install via Composer, construct the SmsClient with your api_sms token, and call sendSms — Unicode, lookup and verify workflows are covered by separate clients in the same package. **Language:** en **Audience:** developer **TLDR:** Install with composer require instasent/instasent-php-lib (or from the GitHub ZIP) and use SmsClient with your api_sms token: sendSms, sendUnicodeSms for text outside GSM-7, and getSmsById. The same package has LookupClient (doLookup) and VerifyClient, which requests a code (requestVerify) and checks it (checkVerify). Requires PHP 5.2.3 or later. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/php/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/php.md (Markdown) The PHP SDK wraps SMS, lookup and verify into small focused clients. Most integrations only need `SmsClient`. ## Installation ### Via Composer ```bash composer require instasent/instasent-php-lib ``` ### Via ZIP Download the source from [GitHub](https://github.com/instasent/instasent-php-lib/zipball/master), drop the folder into your project and require the files directly: ```php require_once __DIR__ . '/path/to/lib/Abstracts/InstasentClient.php'; require_once __DIR__ . '/path/to/lib/SmsClient.php'; ``` ## Send an SMS ```php sendSms('test', '+34647000000', 'test message'); echo $response['response_code']; echo $response['response_body']; ``` ### Unicode For SMS containing characters outside GSM-7 (accents, emoji) use `sendUnicodeSms`: ```php $response = $client->sendUnicodeSms('test', '+34647000000', 'Unicode test: ña éáíóú 😀'); ``` ## Retrieve an SMS ```php getSmsById('smsId'); echo $response['response_code']; echo $response['response_body']; ``` ## Lookup ```php doLookup('+34666000000'); echo $response['response_code']; echo $response['response_body']; ``` ## Verify Verify is a two-step workflow: request a code, then check it against the user input. ### Request a code ```php requestVerify('test', '+34647000000', 'Your code is %token%', 6, 300); echo $response['response_code']; echo $response['response_body']; ``` The fourth argument is the code length, the fifth is the validity in seconds. ### Check the code ```php $client = new Instasent\VerifyClient(getenv('INSTASENT_TOKEN')); $response = $client->checkVerify($requestVerifyId, $token); $body = json_decode($response['response_body']); if ($body->entity->status === 'verified') { // success } else { // handle pending / failed / expired } ``` ## Requirements PHP ≥ 5.2.3. ## Getting help For help installing or using the library, contact [support@instasent.com](mailto:support@instasent.com). Bugs and feature requests belong on the [GitHub repository](https://github.com/instasent/instasent-php-lib). --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/python # Python SDK Official Python client for the Instasent A2P Messaging API. Install via pip, instantiate the client with your api_sms token, and call send_sms. **Language:** en **Audience:** developer **TLDR:** Install with pip install instasent (or python setup.py install from source), create instasent.Client with your api_sms token and call send_sms(sender, to, text); get_sms and get_sms_by_id read messages. Anything beyond these methods is a plain REST call against the API reference. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/python/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/python.md (Markdown) Minimal client over the A2P Messaging HTTP endpoints. Anything beyond the methods below is a plain REST call — the [Reference](/a2p-messaging-api/http/reference) covers the whole surface. ## Installation ### Via pip ```bash pip install instasent ``` ### From source ```bash python setup.py install ``` ## Send an SMS ```python import os import instasent client = instasent.Client(os.environ["INSTASENT_TOKEN"]) response = client.send_sms("My company", "+34666666666", "test message") print(response["response_code"]) print(response["response_body"]) ``` ## Available methods ```python client.send_sms(sender, to, text) client.get_sms(page, per_page) client.get_sms_by_id(message_id) ``` ## Getting help For help installing or using the library, contact [support@instasent.com](mailto:support@instasent.com). Bugs and feature requests belong on the [GitHub repository](https://github.com/instasent). --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/ruby # Ruby SDK Official Ruby client for the Instasent A2P Messaging API. Install the instasent gem, instantiate Instasent::Client with your api_sms token, and call send_sms. **Language:** en **Audience:** developer **TLDR:** Install the instasent gem (gem install instasent, or add it to your Gemfile and run bundle install), create Instasent::Client with your api_sms token and call send_sms(sender, to, text); get_sms and get_sms_by_id read messages. Anything else is a plain HTTP call against the API reference. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/ruby/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/ruby.md (Markdown) Minimal client over the A2P Messaging HTTP endpoints. For anything outside the methods below, drop back to a plain HTTP call — the [Reference](/a2p-messaging-api/http/reference) documents every endpoint. ## Installation ### Via RubyGems ```bash gem install instasent ``` ### Via Bundler Add the gem to your `Gemfile`: ```ruby gem "instasent" ``` Then run `bundle install`. ## Send an SMS ```ruby require "instasent" client = Instasent::Client.new(ENV["INSTASENT_TOKEN"]) response = client.send_sms("My company", "+34666666666", "test message") puts response["response_code"] puts response["response_body"] ``` ## Available methods ```ruby client.send_sms(sender, to, text) client.get_sms(page, per_page) client.get_sms_by_id(message_id) ``` ## Getting help For help installing or using the library, contact [support@instasent.com](mailto:support@instasent.com). Bugs and feature requests belong on the [GitHub repository](https://github.com/instasent). --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context. --- URL: https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/java # Java SDK Official Java client for the Instasent A2P Messaging API. Download the JAR, construct an InstasentClient with your api_sms token, and call sendSms. **Language:** en **Audience:** developer **TLDR:** Download the instasent-java-lib JAR (or the JAR with dependencies) and add it to your classpath, create an InstasentClient with your api_sms token and call sendSms(sender, to, text, callback); getSms and getSmsById read messages. Anything else is a plain HTTP call against the API reference. **Docs index (every page):** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt **This zone's index:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/llms-full.txt **This page:** https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/java/ (HTML) · https://staging-instasent-docs-nextjs.oscar-284.workers.dev/a2p-messaging-api/sdks/java.md (Markdown) Thin client over the A2P Messaging HTTP endpoints. For anything outside the methods below, use a plain HTTP client — the [Reference](/a2p-messaging-api/http/reference) documents every endpoint. ## Installation Download the JAR and add it to your project's classpath: - [JAR](https://github.com/instasent/instasent-java-lib/releases/download/0.1.4/instasent-java-lib.jar) - [JAR with dependencies](https://github.com/instasent/instasent-java-lib/releases/download/v0.1.4/instasent-client-0.1.4-jar-with-dependencies.jar) ## Send an SMS ```java import com.instasent.InstasentClient; import java.io.IOException; import java.util.Map; public class Main { public static void main(String[] args) throws IOException { InstasentClient client = new InstasentClient(System.getenv("INSTASENT_TOKEN"), true); Map response = client.sendSms("My company", "+34666666666", "test message"); System.out.println(response); } } ``` ## Available methods ```java client.sendSms(sender, to, text, callback); client.getSms(page, perPage); client.getSmsById(messageId); ``` ## Getting help For help installing or using the library, contact [support@instasent.com](mailto:support@instasent.com). Bugs and feature requests belong on the [GitHub repository](https://github.com/instasent/instasent-java-lib). --- This is one page of the Instasent documentation. For the complete machine-readable index of every guide and API reference, fetch https://staging-instasent-docs-nextjs.oscar-284.workers.dev/llms.txt — start there for full context.