--- title: Sending messages | Sendblue Docs description: Outbound messages documentation | iMessage for Business --- On our free plans, a user must be verified as a contact on your account before their messages will be routed to you. This can be done by adding them as a contact through the dashboard. To remove this restriction, [talk to us](https://sendblue.com/request-a-demo) about our dedicated plans. The Sendblue API conforms to REST API standards. Using it, you can send outbound iMessage and SMS to any enabled phone globally. In this documentation, we will cover how to: 1. [Send iMessages to a destination phone number](#sending-imessages) 2. [Track the status of your message using webhooks](#status-callback) ## Sending iMessages There are two endpoints for sending messages: - To send a message to a single phone number, use the [/send-message](/api/resources/messages/methods/send/index.md) endpoint. - To send a message to a group, use the [/send-group-message](/api/resources/groups/methods/send_message/index.md) endpoint. ### Request Parameters | Parameter | Type | Required | Description | | ---------------- | -------- | -------- | ------------------------------------------------------------------------------------------ | | number | `string` | Yes | E.164 formatted phone number of the recipient (e.g., “+19998887777”) | | from\_number | `string` | Yes | E.164 formatted phone number to send from (must be a number on your account) | | content | `string` | No | The message content to send | | media\_url | `string` | No | URL to media file to send (see [Sending files](#sending-files)) | | send\_style | `string` | No | Expressive message style (see [Expressive messages](/guides/expressive-messages/index.md)) | | status\_callback | `string` | No | URL to receive status updates for this message | Either `content` or `media_url` must be provided. You can send both together. ## Sending files You can send images using the `media_url` parameter. This URL should be a CDN link pointing to an image. The URL must end with the proper file extension. We recommend sticking to the standard PascalCase naming convention. So if your image is named “Welcome Image”, we recommend uploading it to your CDN as “WelcomeImage.png”. File size limits vary by message type: | Service | Recommended | Maximum | | -------- | ----------- | ------------------------------------------ | | iMessage | 20 MB | 100 MB | | SMS | — | 5 MB | | RCS | — | 5–100 MB (varies by carrier and file type) | We recommend keeping attachments under 20 MB for the most reliable delivery across all message types. The 100 MB maximum applies to all file types including videos when sending via iMessage. Files with the `.caf` extension are rendered as voice memos on the recipient’s device. All standard video file types (e.g., `.mp4`, `.mov`) render as video. See the [Voice notes guide](/guides/voice-notes/index.md) for details. media\_url does not support [signed urls](https://cloud.google.com/storage/docs/access-control/signed-urls#:~:text=about%20request%20signing.-,Overview,specific%20actions%20on%20a%20resource.), please use the [media object](/api/resources/media_objects/methods/upload/index.md) pattern for that. ## Status Callback Sendblue will POST the endpoint you provide in `status_callback` whenever the status of a message changes to one of the following: - REGISTERED - PENDING - DECLINED - QUEUED - ACCEPTED - SENT - DELIVERED - ERROR Below is an example of the POST body that is sent to the status\_callback URL for a sent message: ``` { "accountEmail": "your-account-email", "content": "Hello world!", "is_outbound": true, "status": "SENT", "error_code": null, "error_message": null, "error_reason": null, "message_handle": "5a17319e-cbcf-443e-897e-d8b0c04b1b09", "date_sent": "2025-12-12T15:35:35.410Z", "date_updated": "2025-12-12T15:35:35.410Z", "from_number": "+18649820355", "number": "+19998887777", "to_number": "+19998887777", "was_downgraded": null, "plan": "dedicated", "media_url": "", "message_type": "message", "group_id": "", "participants": [], "send_style": "", "opted_out": false, "error_detail": null, "sendblue_number": null, "service": "iMessage", "group_display_name": null } ``` You must send a response to our server in order to avoid receiving multiple webhook calls. ## Message Status Resolution | Callback Body Field | Type | Description | | -------------------- | --------- | --------------------------------------------------------------------------------------------- | | accountEmail | `string` | Associated account email | | content | `string` | Message content | | is\_outbound | `boolean` | True if message is sent, false if message is received | | status | `string` | The current status of the message | | error\_code | `int` | Error code (null if no error) | | error\_message | `string` | Descriptive error message (null if no error) | | error\_reason | `string` | Additional error context (null if no error) | | error\_detail | `string` | Detailed error information (null if no error) | | message\_handle | `string` | Sendblue message handle | | date\_sent | `string` | ISO 8601 formatted date string of the date this message was created | | date\_updated | `string` | ISO 8601 formatted date string of the date this message was last updated | | from\_number | `string` | E.164 formatted phone number string of the message dispatcher | | number | `string` | E.164 formatted phone number string of your end-user (not the Sendblue-provided phone number) | | to\_number | `string` | E.164 formatted phone number string of the message recipient | | was\_downgraded | `boolean` | True if the end user does not support iMessage, null otherwise | | plan | `string` | Value of the Sendblue account plan | | media\_url | `string` | A CDN link to any media attached to the message | | message\_type | `string` | Type of message (e.g., “message”) | | group\_id | `string` | Group identifier (empty string for non-group messages) | | participants | `array` | Array of participant phone numbers | | send\_style | `string` | Expressive message style if used (empty string if none) | | opted\_out | `boolean` | True if the recipient has opted out | | sendblue\_number | `string` | The Sendblue phone number used | | service | `string` | The messaging service used (e.g., “iMessage”, “SMS”) | | group\_display\_name | `string` | Display name for group chats (null for non-group messages) | ### Status The status field will report the status. There are currently 8 different possible statuses: | Status | Description | | -------------- | --------------------------------------------------------------------------------------------------------------- | | **REGISTERED** | The message has been registered in our system | | **PENDING** | The message has reached our servers | | **DECLINED** | The message was rejected for some reason (see `error_code` and `error_message` for details) | | **QUEUED** | The message has passed validation and has been queued for delivery | | **ACCEPTED** | The message has been dequeued and is being sent | | **SENT** | The message has been sent (terminal state for SMS, or if the receiver is not connected to the iMessage network) | | **DELIVERED** | The message has been delivered (terminal state for iMessage or RCS) | | **ERROR** | The message has failed to send, see the `error_code` field to understand why | ### Error Codes Any Code besides 0 or null is a failure. Some codes are not yet documented. | Code | Description | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 4000 | Validation Error: see `error_message` field | | 4001 | Rate Limit Exceeded | | 4002 | Blacklisted Number (e.g. 911) | | 5000 | Internal Error | | 5003 | Server Rate Exceeded | | 5509 | Rate limit window exceeded — reduce send frequency or wait for the current window to reset | | 10001 | Message failed to send | | 10002 | Failed to resolve message status | | SMS\_LIMIT\_REACHED | Sendblue’s internal SMS sending limits exceeded. Wait for the limit window to reset before sending additional SMS messages. See [Limits & Queues](/limits/index.md) for details. | ## Limits Messages sent using Sendblue must be less than 18996 characters in length. If you have larger requirements we recommend breaking the message up into several, smaller messages. ## Useful Information ### From Number The `from_number` parameter is required when sending messages. This must be a phone number that is assigned to your Sendblue account. Use the `GET /api/lines` endpoint to retrieve all phone lines on your account. Once you specify a `from_number` for the first message to a contact, you must continue using `from_number` for all subsequent messages to that contact. Sendblue focuses on message deliverability and end-user experience. Each of your users will only ever interact with your business through one Sendblue phone number. However, different clients may see different numbers for texts sent from your company. See [Limits & Queues](/limits/index.md) for more info. If you have multiple lines, you are responsible for implementing your own round-robin or load-balancing logic. Sendblue does not automatically distribute outbound messages across lines. See [Number Pooling](/limits/#number-pooling/index.md) for details. ## SMS Fallback Sendblue automatically falls back from iMessage to SMS when the recipient does not have iMessage enabled. This happens at no extra cost. You can detect when a message was downgraded by checking: - **`service`** field: Will be `"sms"` instead of `"iMessage"` if the message was sent via SMS. - **`was_downgraded`** field: Will be `true` if the end user does not support iMessage. If a message with a large attachment is downgraded to SMS, Sendblue will attempt to compress the file to fit within the 5 MB SMS limit. If compression fails, the message will fail to send. To avoid unexpected fallback behavior, you can use the [Lookup API](/guides/check-imessage-support/index.md) to check a recipient’s service type before sending. There is no option to disable SMS downgrade at the system level. ### Handle The message handle is used so that you can query the status of each message at a later date. This is especially useful in high volume message\_status=QUEUED cases or TIMEOUT cases.