LogoTailride
Settings

Webhooks

Receive real-time HTTP notifications when your invoices are processed in Tailride.

Overview

With Tailride Webhooks, you can stay updated on your documents as they move through the system. Whenever a subscribed event occurs — for example, an invoice finishes processing — Tailride sends an HTTP POST request with a JSON payload to the URL you specify.

This lets you seamlessly integrate Tailride with your own systems and applications: push processed invoices into your ERP, trigger approval flows, archive files, or notify your team.

Relevant for automation and workflow tools such as Zapier, Make, n8n, and others.

Availability

Webhooks are available on paid plans and can be managed by the root user of the account.

Getting Started

Access Webhooks Settings

Open your Tailride dashboard and navigate to Settings → Webhooks.

Create a New Webhook

Fill in the inline form and click Add webhook:

  • Name — a descriptive name for your webhook (e.g. "My CRM").
  • URL — the HTTPS endpoint where you want to receive POST requests (e.g. https://api.yourcompany.com/webhooks/tailride).
  • Event — the event this webhook subscribes to.
  • Headers (optional) — custom HTTP headers to send with every delivery, e.g. an API key your endpoint requires. See Custom Headers.

You can create up to 10 webhooks per account.

Save the Signing Secret

Each webhook gets its own signing secret (whsec_...). Use the copy button next to the webhook to copy it at any time, and the rotate button to issue a new one (the old secret stops working immediately). Use this secret to verify that incoming requests really come from Tailride — see Verifying Signatures.

You can pause a webhook at any time with the toggle — deliveries stop until you re-enable it — or delete it entirely.

Events

Tailride currently supports the following events:

invoice.processed

Fired when a document has been fully processed and recognized as an invoice: text extracted, fields parsed, amounts and vendor identified. This happens shortly after the document reaches Tailride via email scanning, forwarding, manual upload, the browser extension, or bot uploads (Telegram/WhatsApp).

invoice.updated

Fired when an already-processed invoice changes afterwards: fields edited in the dashboard (amount, vendor, tags, payment status, and so on), a supporting document attached or removed — whether uploaded manually or matched automatically by Tailride (e.g. a receipt recognized as belonging to the invoice, which also flips its payment status to paid).

More event types will be added over time; each webhook subscribes to a specific event, so you can point different events at different endpoints.

Delivery and Retries

  • Deliveries are HTTP POST requests with a JSON body and Content-Type: application/json.
  • Your endpoint should respond with a 2xx status code within 10 seconds.
  • On failure, Tailride retries the delivery up to 3 times in total (with short delays between attempts). Network errors, timeouts, and 408/429/5xx responses are retried; any other 4xx response is treated as a rejection and is not retried.
  • Redirects are not followed — configure the final URL directly.
  • Because of retries, your endpoint may occasionally receive the same event more than once. Use data.invoice.id to deduplicate.

Custom Headers

If your endpoint requires authentication or other fixed headers, you can attach up to 5 custom headers to each webhook. They are sent with every delivery alongside the standard Tailride headers.

Typical uses:

  • x-make-apikey: <key> — Make.com custom webhook with API-key verification enabled.
  • Authorization: Bearer <token> — endpoints behind your own auth middleware or an API gateway.

You can define headers when creating a webhook, or add and edit them later via the options button next to an existing webhook.

Rules:

  • Name — must be a valid HTTP header name (letters, digits, - and other token characters), max 128 characters, no duplicates within one webhook.
  • Value — required, single line, max 1024 characters.
  • Reserved names are rejected: Host, Content-Type, Content-Length, User-Agent, Connection, Transfer-Encoding, and anything starting with X-Tailride- — these are always set by Tailride itself, and overriding them would break delivery or signature verification.

Header values are stored encrypted

Header values usually carry credentials (API keys, bearer tokens), so Tailride stores them encrypted at rest, the same way as the signing secret. Like the secret, they remain visible in the webhook settings to the account's root user.

JSON Payload

invoice.processed

When the webhook is triggered, you'll receive a JSON payload like this:

{
  "event": "invoice.processed",
  "timestamp": "2026-07-03T10:15:00.000Z",
  "data": {
    "invoice": {
      "id": "66a1f0c2e4b09a35d8f11a42",
      "from": "billing@acme.com",
      "receivedDate": "2026-06-28T08:12:00.000Z",
      "invoiceId": "INV-2041",
      "invoiceDate": "2026-06-27T00:00:00.000Z",
      "processedDate": "2026-06-28T08:15:42.000Z",
      "documentType": "invoice",
      "amount": 129.9,
      "currency": "EUR",
      "payment": { "status": "paid" },
      "subscription": {
        "isRecurring": true,
        "frequency": "monthly",
        "nextBillingDate": "2026-07-27T00:00:00.000Z",
        "variableAmount": false
      },
      "items": [
        {
          "description": "Team plan subscription",
          "quantity": 1,
          "unitPrice": 108.25,
          "category": "Software",
          "taxRate": 20,
          "taxAmount": 21.65,
          "taxType": "vat",
          "discount": null,
          "subtotal": 108.25,
          "total": 129.9
        }
      ],
      "tags": ["software", "subscription"],
      "vendor": {
        "name": "Acme Corporation",
        "short": "Acme",
        "contactPerson": null,
        "email": "billing@acme.com",
        "phone": null,
        "website": "https://acme.com",
        "address": "1 Acme Way",
        "city": "Dublin",
        "state": null,
        "zipCode": "D01 X2Y3",
        "country": "Ireland",
        "countryCode": "IE",
        "businessId": null,
        "taxId": "IE1234567X"
      },
      "recipient": {
        "name": "Your Company Ltd",
        "email": "office@yourcompany.com",
        "address": "42 Main Street, Berlin",
        "countryCode": "DE",
        "taxId": "DE987654321"
      },
      "attachment": {
        "url": "https://s3.eu-central-1.amazonaws.com/…?X-Amz-Signature=…",
        "filename": "2026-06-27_acme_INV-2041.pdf",
        "mimeType": "application/pdf",
        "expiresAt": "2026-07-04T10:15:00.000Z"
      },
      "supportingDocuments": [
        {
          "url": "https://s3.eu-central-1.amazonaws.com/…?X-Amz-Signature=…",
          "filename": "purchase-order-77.pdf",
          "mimeType": "application/pdf",
          "type": "purchase_order",
          "expiresAt": "2026-07-04T10:15:00.000Z"
        }
      ]
    }
  }
}

invoice.updated

The invoice.updated payload is intentionally light: it carries the invoice id and a field-level list of changes. Fetch or match the full invoice data from your copy of the earlier invoice.processed event.

{
  "event": "invoice.updated",
  "timestamp": "2026-07-03T12:40:00.000Z",
  "data": {
    "invoice": { "id": "66a1f0c2e4b09a35d8f11a42" },
    "changes": [
      {
        "field": "payment.status",
        "previous": "unpaid",
        "current": "paid"
      },
      {
        "field": "supportingDocuments",
        "previous": null,
        "current": {
          "documentId": "55171a92-e62d-428d-a0aa-9e5a9c9c7786",
          "filename": "receipt-2041.pdf",
          "mimeType": "application/pdf",
          "type": "receipt"
        }
      }
    ]
  }
}

Each entry in changes has:

field string The invoice field that changed. Top-level field name (e.g. "amount", "tags", "vendor") or a dotted path for targeted changes (e.g. "payment.status"). Supporting-document attachments and removals are reported as "supportingDocuments" with the affected document in current (added) or previous (removed).

previous any The value before the change (null when the value was absent, e.g. a newly attached document).

current any The value after the change (null when the value was removed).

JSON Field Documentation

Field-by-field reference for the invoice.processed payload above.

event string The event type that triggered this delivery (e.g. "invoice.processed").

timestamp string ISO 8601 time when the event payload was generated.

data.invoice.id string Unique identifier of the invoice in Tailride. Stable across redeliveries — use it for deduplication.

data.invoice.from string or null Who the document came from (usually the sender's email address or name).

data.invoice.receivedDate string or null When the document reached Tailride (email date or upload time). ISO 8601.

data.invoice.invoiceId string or null The invoice number printed on the document, typically assigned by the vendor.

data.invoice.invoiceDate string or null The invoice date extracted from the document. ISO 8601.

data.invoice.processedDate string or null When Tailride finished processing the document. ISO 8601.

data.invoice.documentType string or null The type of document: "invoice", "receipt", "credit_note", or "other".

data.invoice.amount number or null The total amount charged in the document.

data.invoice.currency string or null Currency code of the transaction (e.g. "EUR").

data.invoice.payment.status string or null Payment status: "paid", "unpaid", "overdue", "partial", "cancelled", "pending", or "other".

data.invoice.subscription object or null Recurring-payment details: isRecurring (boolean), frequency ("monthly", "yearly", "other"), nextBillingDate, variableAmount.

data.invoice.items array Line items extracted from the document. Each item includes description, quantity, unitPrice, category, taxRate, taxAmount, taxType, discount, subtotal, and total. When a line's subtotal or total is not printed on the document, Tailride derives it from quantity × unitPrice (and the tax rate, when known).

data.invoice.tags array of strings Tags assigned to the invoice (default and AI-generated tags, plus any set by your rules).

data.invoice.vendor object or null Structured vendor details extracted from the document: name, short, contactPerson, email, phone, website, address, city, state, zipCode, country, countryCode, businessId, taxId. Fields that could not be extracted are null.

data.invoice.recipient object or null Who the invoice was issued to: name, email, address, countryCode, taxId.

data.invoice.attachment object or null The original document file. Contains a temporary download url, the filename, the mimeType, and expiresAt — see File Download Links below. null when no file is stored for the invoice.

data.invoice.supportingDocuments array Additional files attached to the invoice (purchase orders, contracts, receipts, credit notes). Same shape as attachment plus a type field: "purchase_order", "contract", "receipt", "credit_note", or "other".

Download links expire after 24 hours

The url fields in attachment and supportingDocuments are temporary pre-signed links. They are valid for 24 hours from the moment the event is generated — the exact expiry time is provided in the expiresAt field of each file object.

If your system processes events asynchronously, download the files within that window (or store the payload and fetch the files promptly). After the link expires, the file remains available in your Tailride dashboard.

Anyone in possession of a link can download the file until it expires, so treat webhook payloads as confidential.

Verifying Signatures

Every delivery is signed with your webhook's secret so you can verify it genuinely comes from Tailride. Each request includes these headers:

  • X-Tailride-Event — the event type.
  • X-Tailride-Timestamp — Unix time (seconds) when the request was sent.
  • X-Tailride-Signaturev1=<hex>, an HMAC-SHA256 of "{timestamp}.{rawBody}" computed with your signing secret.

To verify a request:

const crypto = require("crypto");
 
function verifyTailrideSignature(req, secret) {
  const timestamp = req.headers["x-tailride-timestamp"];
  const signature = req.headers["x-tailride-signature"]; // "v1=abc123..."
 
  const expected =
    "v1=" +
    crypto
      .createHmac("sha256", secret)
      .update(`${timestamp}.${req.rawBody}`)
      .digest("hex");
 
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Reject requests whose signature does not match, and optionally reject timestamps older than a few minutes to protect against replay.

That's it! You are now set up to receive real-time updates about your invoices through Tailride Webhooks. If you have any questions or need further assistance, feel free to reach out to our support team.

On this page