You can take advantage of IO Phone programmatically through our API. This article covers sending a text message with POST /text and receiving delivery receipts and inbound messages on your own server through a callback URL (webhook).
| Method | URL | Package |
|---|---|---|
| POST | https://rental.software/api6/text | WP/API and IO Phone (at least one IO Phone number on your account) |
Base URL: https://rental.software/api6. Every request needs your apiKey.
Limits
While texting plans are unlimited, we do throttle and have limits in place on an account level (regardless of location or phone number) for billable outbound messages to prevent abuse, whether intentional or accidental.
| Limit | Value |
|---|---|
| Text messages per hour | 200 |
| Text messages per day | 300 |
| Text messages per week | 1,500 |
| API requests | The normal API rate limits apply. See API - Errors and Rate Limits |
If a texting limit is hit, you may still send manual text messages, including through the API but not via an automation, at the rate of 1 message every 5 minutes. Certain system text messages sent on your behalf, such as wind alerts, are unbillable and are not counted in these limits.
Permissions
The only permission required is that you either do not set restrictions by worker on the number you want to use, or, if you are setting these restrictions, you also enable the API worker profile you are using to have access. On multi-location accounts the API key also needs access to a location the number belongs to.
POST /text
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| toNumber | string | Yes | The phone number the message should be sent to. E.164 format (for example +15554447777) is recommended but not required |
| text | string | Yes | The contents of the text message. Must be 2048 characters or less |
| fromNumber | string | No | One of your IO Phone numbers that the message should be sent from. E.164 format is recommended but not required. If omitted, an IO Phone number of yours will be chosen automatically |
| callbackUrl | string | No | The URL where webhook requests will be sent. See the callback notes below |
| country | string | No | Default "US". Optional country code with which to force toNumber to be parsed, in ISO 3166-1 alpha-2 format. It's recommended to only specify this parameter if the country of origin of toNumber is known and its format is nonstandard or unknown |
| customKey | string | No | An optional custom string value that, when specified, will be returned in the subsequent outbound message webhook. This can be useful for tracking purposes. Must be 32 characters or less |
| media | array of strings | No | An optional array of URLs that, when specified, will be included as media attachments as part of the message (MMS). Each URL must not exceed 4096 characters |
How the Callback URL Works
- http or https is required. https (TLS) is highly recommended.
- The most recent callback URL that you've sent is stored on our server (one per fromNumber).
- If omitted, the most recent callback URL associated with fromNumber will be used.
- The callback URL can be cleared from our server by sending it to us as an empty string ("").
- If a callback URL has never been specified or was cleared for a given fromNumber, webhooks will not be sent.
- Once a callback URL is stored for a number, you receive a webhook for every message on that number, inbound and outbound.
Response Schema
| Field | Type | Sent when | Description |
|---|---|---|---|
| id | integer | Accepted | The unique ID associated with the message |
| time | string | Accepted | The UTC timestamp of the message, in ISO 8601 format |
| segmentCount | integer | Accepted | The number of segments that the message was broken into before sending over to carrier networks |
| direction | string | Accepted | The direction of the message, either in or out |
| to | string | Accepted | The phone number the message was sent to, in E.164 format |
| from | string | Accepted | The phone number the message was sent from, in E.164 format |
| text | string | Accepted | The text message content that was sent to the recipient |
| media | array | Accepted | The list of URLs that were sent along to the recipient as media attachments (if specified in the request) |
| status | integer | Always | The status code associated with the request. If the message was accepted, 202 should be expected. Errors will return a status code from 400 to 599 |
| message | string | Failed | A message describing the error |
| request_time | integer | Accepted | The current request time as a Unix timestamp in seconds |
Example Request
POST https://rental.software/api6/text?apiKey=YOUR_API_KEY
Content-Type: application/json
{
"toNumber": "+15554447777",
"fromNumber": "+15551112222",
"text": "Hello world",
"callbackUrl": "https://my-callback-url/path",
"media": ["https://my-server/image.jpg"]
}Example Responses
Successful response:
200 OK
Content-Type: application/json
{
"id": 1753065,
"time": "2023-09-14T17:28:18.709287Z",
"segmentCount": 1,
"direction": "out",
"to": "+15554447777",
"from": "+15551112222",
"text": "Hello world",
"status": 202,
"media": ["https://my-server/image.jpg"],
"request_time": 1694712498
}Error responses:
400 Bad Request
Content-Type: application/json
{
"status": 400,
"message": "Required parameter 'text' cannot be empty"
}403 Forbidden
Content-Type: application/json
{
"status": 403,
"message": "This account is not authorized to send text messages from this number ('fromNumber')"
}Common Errors
| Status | Message | What to do |
|---|---|---|
| 400 | Missing required parameter 'toNumber' (or 'text') | Send both required parameters |
| 400 | Could not parse 'toNumber'. Must be sent in a valid format | Send the number in E.164 format, or add country |
| 400 | Length of 'text' parameter cannot exceed 2048 characters | Shorten the message |
| 400 | This text contains N segments but must not exceed 15 | Shorten the message. Long messages are split into segments, and a message can be at most 15 segments |
| 400 | The recipient has not opted-in to receiving text messages | The recipient has opted out of texts, so the message cannot be sent |
| 400 | Unable to find a number to send this text message from | Send a fromNumber that the API key is allowed to use |
| 403 | This account is not authorized to send text messages from this number ('fromNumber') | Check the worker restrictions on the number |
| 429 | You are sending messages too quickly. Please wait and try again later. | A texting limit was hit. Wait before sending again |
Webhook Examples
Webhooks are sent to your callback URL as a POST request with a JSON body:
POST {YOUR_CALLBACK_URL}
Content-Type: application/jsonYour server should answer quickly. A webhook that fails is not sent again.
Outbound Message Delivery Receipt
{
"id": 1753064,
"time": "2023-09-14T15:59:38.406172698Z",
"type": "message-delivered",
"description": "Message delivered to carrier.",
"segmentCount": 1,
"direction": "out",
"to": "+15554447777",
"from": "+15551112222",
"text": "Hello world",
"customKey": "custom string",
"media": ["https://rental.software/your-outbound-hosted-image.jpg"]
}Inbound Message Received
{
"id": 1753060,
"time": "2023-09-14T13:52:27.881412Z",
"type": "message-received",
"description": "Incoming message received",
"segmentCount": 1,
"direction": "in",
"to": "+15551112222",
"from": "+15554447777",
"text": "Hello world",
"media": ["https://rental.software/inbound-hosted-image.jpg"]
}Outbound Message Failure
{
"id": 1753063,
"time": "2023-09-14T17:56:57.406573455Z",
"type": "message-failed",
"description": "invalid-destination-address",
"segmentCount": 0,
"direction": "out",
"to": "+15554447777",
"from": "+15551112222",
"errorCode": 4720,
"text": "Hello world",
"customKey": "custom string"
}Testing
You can test sending through the API by using a fromNumber of (999) 999-9999. Test SMS messages are not sent, but we simulate as if they are, and will also initiate a send to your callback URL if you have specified one in the same request. Test messages are not counted as API transactions. See the example send and callback below.
Send:
{
"id": 5553,
"time": "2023-09-18T15:49:43.052Z",
"segmentCount": 1,
"direction": "out",
"to": "+15554447777",
"from": "+19999999999",
"text": "API Test Request",
"status": 202,
"request_time": 1695066583
}Callback:
{
"id": 5553,
"time": "2023-09-18T15:49:43.052Z",
"type": "message-delivered",
"description": "Message delivered to carrier.",
"segmentCount": 1,
"direction": "out",
"to": "+15554447777",
"from": "+19999999999",
"text": "API Test Request"
}