Webhook Reference

    Delivery Receipt Webhook

    Configure an endpoint URL in your Texto dashboard and we'll POST a JSON event every time a delivery receipt is recorded for one of your messages. Signed with HMAC-SHA256, retried on failure, and idempotent by design.

    Getting started with SMS delivery receipt webhooks

    1. 1

      In the Texto dashboard, go to Developer → Webhooks, paste your endpoint URL, and enable the webhook.

    2. 2

      Copy your signing secret. Store it as TEXTO_WEBHOOK_SECRET in your server environment — never commit it to source control.

    3. 3

      Deploy an HTTPS endpoint that verifies the X-Texto-Signature header (snippet below) and returns a 2xx response within 15 seconds.

    Trigger

    Fired once per delivery receipt for any message on your account, including team-member sends.

    Request

    POST
    <your endpoint URL>

    Headers

    HeaderValueDescription
    Content-Typeapplication/jsonBody is always UTF-8 JSON.
    X-Texto-Eventmessage.dlrEvent type. Currently always message.dlr.
    X-Texto-DeliveryUUIDUnique per delivery. Same UUID is sent on retries — dedupe on this.
    X-Texto-Signaturesha256=<hex>HMAC-SHA256 of the raw body using your signing secret. Only sent when signing is enabled.

    Body

    Byte-identical to GET /message/:id on the REST API — same fields, same names, same types. Anything new added there is automatically sent here.

    {
      "message": {
        "id": "8c1f9b2e-1a4c-4f87-9bd2-2d2f6f6f6f6f",
        "recipient": "+61412345678",
        "body": "Hi Sam, your appointment is confirmed for Tue 9am.",
        "status": "delivered",
        "sent_at": "2026-05-06T03:14:22.000Z"
      },
      "delivery_receipt": {
        "status": "delivered",
        "received_at": "2026-05-06T03:14:25.421Z"
      }
    }

    Verifying the signature (Node.js)

    Always verify the signature before trusting the payload. Use the raw request bytes (not a re-serialised JSON string) and a constant-time comparison.

    import crypto from "node:crypto";
    
    app.post("/webhooks/texto", express.raw({ type: "application/json" }), (req, res) => {
      const signatureHeader = req.header("x-texto-signature") || "";
      const expected = crypto
        .createHmac("sha256", process.env.TEXTO_WEBHOOK_SECRET)
        .update(req.body) // raw bytes — not JSON.stringify(parsed)
        .digest("hex");
    
      const provided = signatureHeader.replace(/^sha256=/, "");
    
      const ok =
        provided.length === expected.length &&
        crypto.timingSafeEqual(Buffer.from(provided, "hex"), Buffer.from(expected, "hex"));
    
      if (!ok) return res.status(401).send("invalid signature");
    
      const event = JSON.parse(req.body.toString("utf8"));
      // event.message, event.delivery_receipt …
      res.sendStatus(200);
    });

    Expected response

    Reply with any 2xx status code (typically 200 OK or 204 No Content) to acknowledge receipt. The response body is ignored — you don't need to return JSON. Respond within 15 seconds; longer than that and we treat it as a failure and retry.

    Your responseWhat we do
    2xxMarked delivered. No retry.
    4xxTreated as a failure and retried on the schedule below — fix your endpoint and the next attempt will succeed.
    5xxRetried on the schedule below.
    No response / timeoutRetried on the schedule below.

    Retry schedule

    Up to 3 attempts in total. After the third failure the delivery is dropped and shown as failed in your "Recent deliveries" log.

    AttemptWhen
    1Immediately when the delivery receipt is recorded
    2~1 minute after attempt 1 fails
    3~5 minutes after attempt 2 fails
    After a further ~30 minutes the delivery is given up on

    The same X-Texto-Delivery UUID is sent on every retry, so retried events always look like duplicates of attempt 1 — dedupe on it.

    Idempotency

    Network hiccups can cause your endpoint to receive the same delivery twice. The X-Texto-Delivery header is stable across retries — store and dedupe on it before processing.

    Start receiving delivery receipts

    Configure your webhook endpoint in the Texto dashboard and you're live in minutes.