Webhooks

Every state change, pushed to you. Signed, retried, idempotent.

Tapped emits webhooks the moment a transaction transitions state. Sign them, verify them, act on them, or walk away and we'll retry for 72 hours. Replay any event from the dashboard.

How webhooks work

You register an endpoint URL in the dashboard (or via API). When a subscribed event fires, Tapped delivers a POST to your URL with a JSON body and a set of signing headers. Respond with 2xx within 5 seconds to acknowledge. Any other response (or a timeout) triggers a retry.

One event, one delivery

Every event has a unique id. Use it to deduplicate, if your handler crashes mid-processing, retries may deliver the same event up to ~20 times.

Signature verification

Every delivery includes a Tapped-Signature header. The signature is an HMAC-SHA256 of the raw request body with your endpoint's signing secret. Use the SDK helper; if you're on REST, the verification is eight lines of code.

// Node, manual verification
import { createHmac } from "crypto";

function verify(rawBody, header, secret) {
  const [ts, sig] = header.split(",").map(s => s.split("=")[1]);
  const expected = createHmac("sha256", secret)
    .update(`${ts}.${rawBody}`)
    .digest("hex");
  return expected === sig;
}

Retries & deduplication

Failed deliveries retry on an exponential backoff: 10s, 1m, 5m, 30m, 2h, 6h, 12h, 24h, 48h, 72h. After 72 hours of failures we mark the event failed in the dashboard. You can replay any event, failed or succeeded, with one click.

Order is not guaranteed. Two events with adjacent timestamps may arrive in either order. Design your handler around id, not arrival time.

Replaying events

Any event in the last 30 days can be replayed from the dashboard or via POST /v1/events/:id/redeliver. Replays include a header Tapped-Replayed: true so your handler can log them separately if you wish.

tap.* events

EventWhen it fires
tap.approvedScheme authorised the tap.
tap.declinedScheme declined; includes decline_reason.
tap.reversedTap reversed before settlement (customer re-presented card, partial).

charge.* events

EventWhen it fires
charge.capturedAuthorisation captured; funds committed.
charge.refundedFull or partial refund processed.
charge.failedCapture failed (rare; usually scheme outage).

payout.* events

EventWhen it fires
payout.scheduledPayout queued for disbursement; includes ETA.
payout.paidBank confirmed receipt; includes wire reference.
payout.failedBank rejected (wrong account, frozen). Actionable.

dispute.* events

EventWhen it fires
dispute.openedChargeback received. Includes scheme deadline.
dispute.evidence_required48h before scheme deadline, last call.
dispute.closedWon, lost, or withdrawn. Final state.

submerchant.* events

Tapped Onboard v1 — poll, don't subscribe

The Tapped Onboard API (sub-merchant provisioning) does not emit integrator-facing webhooks in v1. Stripe webhooks are consumed internally; to track onboarding, poll GET /sub-merchants/:externalRef and read status plus the RequirementsProfile. See the onboarding flow and the API reference.

The events below are planned. When integrator-facing onboarding webhooks ship, they'll carry the same status values the REST API returns (received, creating, pending_requirements, enabled, rejected, errored).

EventWhen it firesStatus
submerchant.status.updatedPlanned. The account moved to a new onboarding state.any
submerchant.requirements.updatedPlanned. currentlyDue/pastDue changed; more data is needed.pending_requirements
submerchant.enabledPlanned. Onboarding complete; check charges_enabled/payouts_enabled.enabled
submerchant.rejectedPlanned. The PSP declined the account; see disabledReason.rejected