Skip to main content
When LaraCopilot sends an SMS it registers a callback URL with the gateway. Once the carrier confirms delivery (or failure), the gateway POSTs a delivery receipt — commonly called a DLR — back to that URL. LaraCopilot handles the incoming DLR, looks up the corresponding SmsMessage record by its external message ID, and updates both delivery_status and delivered_at. No further action is required on your side: the status is immediately visible on the message record.

DLR webhook endpoint

LaraCopilot exposes two equivalent URLs that accept inbound DLR callbacks. Both resolve to the same handler logic.
RouteDescription
POST /api/sms-gateways/{smsGateway}/dlrREST API route — recommended for new integrations.
POST /webhooks/jasmin/{smsGateway}/dlrWeb route alias retained for Jasmin gateway compatibility.
The {smsGateway} path segment is the integer ID of the gateway record in LaraCopilot. You typically configure this URL directly in your gateway or include it as the callback_url when calling POST /api/sms-gateways//send.
POST /api/sms-gateways/{smsGateway}/dlr
Path parameters
smsGateway
integer
required
The ID of the SMS gateway that is delivering the receipt.
Payload fields accepted LaraCopilot accepts the DLR payload as either JSON or form-encoded data. It reads the following fields:
message_id
string
The external message identifier returned by the gateway when the SMS was submitted. Also accepted as id or messageId — LaraCopilot checks all three in that order.
status
string
Delivery status string from the gateway. Also accepted as message_status. When neither field is present, LaraCopilot defaults the status to "delivered".
What happens LaraCopilot queries SmsMessage records for a row matching both sms_gateway_id and external_message_id. If a match is found it performs the following updates on that record:
  • delivery_status — set to the lowercased value of status or message_status from the payload (default: "delivered").
  • delivered_at — set to the current timestamp.
  • remote_response — the inbound DLR payload is merged into the existing remote_response object so the full gateway conversation is preserved.
If no matching record is found, LaraCopilot returns 200 OK with {"message": "DLR received"} and takes no further action. Response
{
  "message": "DLR received"
}
The endpoint always returns HTTP 200. Gateway retry logic based on non-2xx responses will not be triggered.

Configuring the callback URL

When you call POST /api/sms-gateways//send, include callback_url in the request body to tell LaraCopilot where the gateway should send the DLR.
Send with explicit callback URL
curl -X POST https://your-instance.laracopilot.com/api/sms-gateways/1/send \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "to_number": "+447911123456",
    "from_sender": "LaraCopilot",
    "message": "Hello from LaraCopilot.",
    "callback_url": "https://your-instance.laracopilot.com/api/sms-gateways/1/dlr"
  }'
When callback_url is omitted, LaraCopilot automatically sets it to:
https://your-instance.laracopilot.com/api/sms-gateways/{id}/dlr
This default means the DLR will always be routed back correctly as long as your LaraCopilot instance is publicly reachable by the gateway.

Message status values

delivery_status on an SmsMessage record moves through the following states:
StatusWhen it is set
queuedImmediately when the send request is received, before the gateway is contacted.
submittedAfter the gateway responds with a success status.
deliveredAfter a DLR callback confirms the message reached the handset.
failedWhen the gateway returns an error, the request times out, or a DLR reports failure.
The status value sent in the DLR payload is lowercased and written directly to delivery_status. If your gateway sends values such as "DELIVRD" or "UNDELIV", those exact lowercased strings will appear on the record.

Example DLR payload

The following is a representative payload that a Jasmin-compatible gateway sends to the callback URL. Your gateway documentation should confirm the exact fields it uses.
Example DLR payload from gateway
{
  "message_id": "gw-msg-00993",
  "status": "delivered",
  "to": "+447911123456",
  "from": "LaraCopilot",
  "submit_date": "2024-03-20T12:00:01Z",
  "done_date": "2024-03-20T12:00:08Z"
}
LaraCopilot extracts message_id and status from this payload. All other fields are merged into remote_response on the matching SmsMessage record for your records.

Full SMS lifecycle

1

Send request

Your application calls POST /api/sms-gateways/{id}/send. LaraCopilot creates an SmsMessage with delivery_status: "queued".
2

Gateway dispatch

LaraCopilot forwards the message to the gateway over HTTP(S) with Basic Auth. The callback_url (your DLR endpoint) is included in the payload as dlr-url.
3

Submitted

On a successful gateway response, delivery_status is updated to "submitted" and submitted_at is recorded. The send API returns the message object at this point.
4

DLR callback

The gateway POSTs a delivery receipt to your callback_url. LaraCopilot matches the message_id, updates delivery_status to "delivered" (or "failed"), and sets delivered_at.

Security: validating callback_token

Without token validation, any party that knows your DLR URL can forge delivery receipts and manipulate message status records.
Each gateway record has a callback_token field. When configuring your gateway in LaraCopilot, set a long random string as the token and configure your gateway to include it in every DLR request — typically as a query parameter or custom header. Your gateway configuration or a reverse-proxy rule should reject DLR requests that do not carry the expected token before they reach LaraCopilot. The callback_token value is available on the gateway object returned by GET /api/sms-gateways/.
Retrieve a gateway's callback_token
curl https://your-instance.laracopilot.com/api/sms-gateways/1 \
  -b cookies.txt