Skip to main content

Reply

Parse webhooks and reply to incoming messages. reply() prefills to/from from the event.
import express from "express";
import { Contiguity } from "contiguity";

const app = express();
const contiguity = new Contiguity();

app.post("/webhook", express.raw({ type: "application/json" }), async (req, res) => {
    if (!contiguity.webhook.verify(req, process.env.CONTIGUITY_WEBHOOK_SECRET)) {
        return res.sendStatus(401);
    }
    const event = contiguity.webhook.parse(req.body);
    if (event.type === "text.incoming.sms" || event.type === "text.incoming.mms") {
        await contiguity.text.reply(event, { message: "Got it, thanks!" });
    }
    if (event.type === "imessage.incoming") {
        await contiguity.imessage.reply(event, { message: "Hi back!" });
    }
    res.sendStatus(200);
});

Verify

contiguity.webhook.verify(rawBody, signatureHeader, secret, toleranceSeconds?)
  • rawBody: The exact raw request body (string or buffer). Never re-serialize JSON (e.g. do not use JSON.stringify(req.body)).
  • signatureHeader: Value of the Contiguity-Signature header. Format: t=<timestamp>,v1=<hex>.
  • secret: Your webhook signing secret.
  • toleranceSeconds: Optional. If set (e.g. 300), rejects if |now - t| > toleranceSeconds.
Returns true if the signature is valid, false otherwise. Verification uses HMAC-SHA256 of t + "." + raw_body with the secret and constant-time comparison to v1.

Parse

contiguity.webhook.parse(rawBody) Parses the raw body as JSON and validates id, type, timestamp. Returns a typed event (v2 format). Throws if invalid.

Example

import { Contiguity } from "contiguity";
const contiguity = new Contiguity("contiguity_sk_..."); // or new Contiguity() for env

// In your route: use raw body only (e.g. express.raw for this route)
const raw_body = req.body; // buffer or string, unparsed
const ok = contiguity.webhook.verify(
  raw_body,
  req.headers["contiguity-signature"],
  process.env.WEBHOOK_SECRET,
  300
);
if (!ok) return res.status(401).send("Invalid signature");
const event = contiguity.webhook.parse(raw_body);
// event.id, event.type, event.timestamp, event.data

Payload format (v2)

All events include:
  • id: string (e.g. evt_...)
  • type: string (event type)
  • timestamp: number (Unix)
  • api_version: string
  • data: object (event-specific)
Event types (examples):
text.incoming.sms, text.incoming.mms, text.delivery.confirmed, text.delivery.failed, imessage.incoming, numbers.substitution, otp.reverse.verified, email.incoming, identity.verification_session.started, identity.verification_session.processing, identity.verification_session.verified, identity.verification_session.failed, identity.verification_session.requires_input, identity.verification_session.manually_approved, identity.verification_session.manually_denied, identity.verification_report.generated.

Signing reference

  • Signed payload: t + "." + raw_body (timestamp t + . + raw body).
  • Algorithm: HMAC-SHA256.
  • Signature: hex-encoded in header as t=<t>,v1=<hex>.
  • Verification: Compute HMAC-SHA256 of signed payload with webhook secret; compare to v1 with constant-time comparison. Optionally enforce timestamp tolerance (e.g. 300 seconds).
See Webhook signing for framework-specific examples.