Inbound SMS & MMS webhooks
Receive an HTTP notification when an SMS or MMS is routed to your ProTexting account. ProTexting sends the message details to the callback URL configured in your dashboard.
Configuration
- Open API – Messaging Settings in your ProTexting account.
- Under SMS & MMS: Webhook Configuration, select Configure Your Webhook.
- Enter your callback URL, select get, post or post-json, and select Check to ACTIVATE.
- Select Create or Update. Confirm the saved URL, method and ACTIVE status.
| Setting | Description |
|---|---|
| Callback URL | Your application's publicly accessible endpoint, for example https://your-domain.example/webhooks/protexting. Use HTTPS. |
| Request method | GET, form-encoded POST or JSON POST. POST-JSON is an HTTP POST with a JSON body. |
| Activation | The webhook must be ACTIVE to forward incoming messages. |
Scope: one configuration per ProTexting account. Separate callback URLs are not configured here for individual keywords, short codes or long numbers. Existing keyword and number routing determines which account receives a message.
If the settings are unavailable, enable the required messaging API app in the Marketplace. Outbound API keys are managed separately under Settings → Developers API Keys.
Request format and authentication
Parameter names are case-sensitive. The selected method determines how the message fields are encoded.
| Dashboard method | HTTP request | Message fields |
|---|---|---|
| get | GET | Query parameters, including type=basic. MMS resources use indexed parameters such as Resource_Url[0]. |
| post | POSTapplication/x-www-form-urlencoded | Form-encoded body. MMS resources use indexed fields such as Resource_Url[0]. |
| post-json | POSTapplication/json | JSON body. Resource_Url is an array. |
All formats include the integration's forwarding token as the access_token query parameter. POST and POST-JSON also include Authorization: Bearer <forwarding_token>. The token is not part of the JSON body.
Validate the configured forwarding token on your server. It may differ from your outbound API token. Keep it in server-side configuration and redact it from request logs.
Payload fields
| Field | JSON type | Description |
|---|---|---|
| Id | integer | Identifier of the received inbox message. Use this with the account context when identifying a message in your application. |
| Contact_Id | integer | Identifier of the sender's contact in your account. Can be 0 when no contact is found. |
| MobilePhone | string | The sender's phone number. Preserve it as a string. This is not the receiving ProTexting number. |
| Message | string | Incoming message text. An MMS can contain both text and media. |
| Type_Id | integer | 1 for SMS; 2 for MMS. |
| Type_Description | string | sms or mms, corresponding to Type_Id. |
| Resource_Url | array of strings | MMS media URLs. Empty for SMS in POST-JSON; GET and form POST omit indexed entries when there are no resources. Media files are not included in the request body. |
The standard payload does not include a receiving-number, keyword or account field. Use your configured endpoint and account context for routing within your application.
Request examples
These examples show requests sent from ProTexting to your endpoint. The callback path, token, numbers, message IDs and media URLs are sample values.
An incoming SMS. MobilePhone is the sender; Id is the inbox message identifier. Resource_Url is empty.
POST /webhooks/protexting?access_token=YOUR_FORWARDING_TOKEN HTTP/1.1Host: your-domain.exampleAuthorization: Bearer YOUR_FORWARDING_TOKENContent-Type: application/json { "Id": 100, "Contact_Id": 1101, "MobilePhone": "12025550123", "Message": "Is order #4821 ready for pickup?", "Type_Id": 1, "Type_Description": "sms", "Resource_Url": []}Endpoint response
Return HTTP 200 OK within 30 seconds. An empty response body is sufficient.
HTTP/1.1 200 OKContent-Length: 0| Response | Delivery result |
|---|---|
| 200 OK | Successful webhook delivery. |
| Any other status | Failed delivery, including 201, 202, 204, redirects and error responses. |
| Timeout or connection failure | Failed delivery. The request timeout is 30 seconds. |
For longer processing, validate the request and save or queue the work in your own application before returning 200. Returning text in the response does not send an SMS reply; use the outbound messaging API to reply.
Delivery and retries
Automatic retries are not supported. The standard integration makes one delivery attempt. An unavailable endpoint, timeout or non-200 response is recorded as a failure. Restoring the endpoint does not automatically replay missed notifications.
The original SMS/MMS is saved in ProTexting before forwarding. Reconcile failed callbacks using your inbox, the webhook log and the List Messages API. If your own processing replays a notification, use the account and inbound Id to prevent duplicate processing.
Implementation examples
The following POST-JSON receivers authenticate and validate the notification, call an application handler and return 200 after the handler succeeds. They use built-in modules and require no additional packages.
Receiver code · Node.js, PHP and Python
Select post-json in the dashboard. Set PROTEXTING_WEBHOOK_TOKEN to the integration's forwarding token and add your processing to handleIncoming or handle_incoming. The included handler logs only the ID, message type and media count.
Save as webhook.mjs. Requires Node.js 18+; uses built-in HTTP and crypto modules only.
// webhook.mjs · Node.js 18+import { createServer } from 'node:http';import { createHash, timingSafeEqual } from 'node:crypto'; const token = process.env.PROTEXTING_WEBHOOK_TOKEN;if (!token) throw new Error('Set PROTEXTING_WEBHOOK_TOKEN');const digest = value => createHash('sha256').update(value).digest(); async function handleIncoming(message) { // Add your application's message processing here. // Log metadata only. Store or queue work in your application as required. console.info('Inbound message: ' + JSON.stringify({ Id: message.Id, Type: message.Type_Description, ResourceCount: message.Resource_Url.length, }));} createServer(async (req, res) => { const reply = status => res.writeHead(status, { 'Content-Length': '0' }).end(); try { const url = new URL(req.url, 'http://localhost'); if (url.pathname !== '/webhooks/protexting') return reply(404); if (req.method !== 'POST') { res.setHeader('Allow', 'POST'); return reply(405); } const bearer = /^Bearer (.+)$/i.exec(req.headers.authorization || '')?.[1]; const supplied = url.searchParams.get('access_token') ?? bearer ?? ''; if (!timingSafeEqual(digest(token), digest(supplied))) return reply(401); if (req.headers['content-type']?.split(';')[0].trim() !== 'application/json') return reply(415); // Sample receiver limits: normal Content-Length requests, up to 64 KiB. const length = Number(req.headers['content-length']); if (!Number.isInteger(length) || length < 1) return reply(411); if (length > 65536) return reply(413); const chunks = []; for await (const chunk of req) chunks.push(chunk); let data; try { data = JSON.parse(Buffer.concat(chunks).toString('utf8')); } catch { return reply(400); } if (!data || Array.isArray(data) || typeof data !== 'object') return reply(400); const id = String(data.Id ?? ''); const type = String(data.Type_Id ?? ''); const media = data.Resource_Url ?? []; if (!/^[1-9][0-9]*$/.test(id) || !['1', '2'].includes(type) || typeof data.MobilePhone !== 'string' || !data.MobilePhone || typeof data.Message !== 'string' || !Array.isArray(media) || !media.every(item => typeof item === 'string')) return reply(400); const message = { Id: id, Contact_Id: data.Contact_Id ?? 0, MobilePhone: data.MobilePhone, Message: data.Message, Type_Id: Number(type), Type_Description: type === '1' ? 'sms' : 'mms', Resource_Url: media, }; // Wait for your application handler to finish before acknowledging. await handleIncoming(message); return reply(200); } catch { console.error('Webhook handler failed'); // Do not log tokens or the payload. return reply(500); }}).listen(Number(process.env.PORT || 8080), '127.0.0.1');These reference receivers accept a JSON body with Content-Length up to 64 KiB. Those are sample limits, not ProTexting service limits. The examples do not implement persistence or duplicate suppression. Deploy your endpoint behind a production HTTPS server.
| Sample response | Condition |
|---|---|
| 200 | The application handler completed successfully. |
| 400 | Malformed JSON or invalid message fields. |
| 401 | Missing or incorrect forwarding token. |
| 415 | The request is not JSON. |
| 500 | The application handler failed. No automatic retry is scheduled. |
Testing and troubleshooting
- Send a test callback: select Test Webhook: SMS & MMS in the webhook settings. This sends sample data to your endpoint without sending an SMS/MMS to a handset.
- Inspect a delivery: open Log in the same section to review the request, HTTP status and response body.
- No callback: confirm that the integration is ACTIVE and the inbound message is routed to the configured account.
- Failed callback: check public reachability, token validation, request parsing, the 200 response and the 30-second timeout. Use the final endpoint URL without a redirect.
Local testing · environment, commands and cURL
Save a receiver using the filename shown in its tab. Set the following variables and start one receiver. Use these synthetic values locally; a localhost address cannot receive callbacks from ProTexting.
export PROTEXTING_WEBHOOK_TOKEN='local-demo-token-only'export PORT=8080Run after saving the Node.js receiver and setting the environment variables above.
node webhook.mjsIn a second terminal, set the same environment variables and run a test request. These commands contact only the local receiver. Valid SMS and MMS requests return 200; an invalid token returns 401.
Returns HTTP 200 for a valid SMS notification.
# Local receiver only. This does not call ProTexting or send an SMS.curl --silent --show-error --include \ 'http://127.0.0.1:8080/webhooks/protexting' \ --header "Authorization: Bearer ${PROTEXTING_WEBHOOK_TOKEN:?Set PROTEXTING_WEBHOOK_TOKEN}" \ --header 'Content-Type: application/json' \ --data-binary '{ "Id": 100, "Contact_Id": 1101, "MobilePhone": "12025550123", "Message": "Is order #4821 ready for pickup?", "Type_Id": 1, "Type_Description": "sms", "Resource_Url": []}'For help with a failed callback, contact support with the inbound message ID, approximate timestamp, selected method and redacted HTTP status or error.
Reference from the ProTexting documentation catalog. Code examples are illustrative; this page does not execute API requests.