Agent skill
Justt Webhooks Skill
Receive and verify Justt webhooks, which follow the Standard Webhooks specification. Use when setting up a Justt chargeback webhook endpoint, getting the whsec_ signing secret, debugging webhook-id / webhook-timestamp / webhook-signature verification, or handling events like chargeback.created, chargeback.status.updated, pre_chargeback_alert.received and subscription.cancel_immediately.failed.
Install this skill
npx skills add hookdeck/webhook-skills --skill justt-webhooks
Justt manages chargebacks and automates disputes for merchants. Its webhooks report chargeback, pre-chargeback alert and CRM-subscription-cancellation events as a signed JSON POST to each endpoint you add in the Justt webhooks dashboard. Signatures follow Standard Webhooks, so each delivery carries webhook-id, webhook-timestamp and webhook-signature headers. Webhooks are not available to every Justt customer yet. The dashboard, message ids and retry/recovery tooling are Svix's.
When to Use This Skill
- How do I receive Justt webhooks?
- How do I verify a Justt webhook signature (
webhook-signature)? - Where do I find my Justt webhook signing secret (
whsec_...)? - How do I handle
chargeback.created,chargeback.status.updatedorpre_events?chargeback_ alert.received - Why is my Justt webhook verification failing, or failing only on retries and replays?
Verification (core)
| Header | Value |
|---|---|
webhook-id | msg_...; identifies the message and is stable across retries. Your idempotency key (the body has no event id) |
webhook-timestamp | Unix seconds |
webhook-signature | One or more space-separated v1,<base64> entries |
Signed content is {webhook-id}.{webhook-timestamp}.{raw body}, HMAC-SHA256, base64 output. The key is the base64-decoded part of the whsec_... secret after the prefix. Compare against every v1, entry; ignore other versions. If webhook-id is absent, read svix-id, svix-timestamp and svix-signature instead, as Hookdeck does. The examples use the standardwebhooks library (npm and PyPI):
const { Webhook } = require('standardwebhooks');
// rawBody: exact request body (Buffer or string), NOT re-serialised JSON
function verifyJustt(rawBody, headers, secret = process.env.JUSTT_WEBHOOK_SECRET) {
const p = headers['webhook-id'] ? 'webhook' : 'svix'; // fall back to the svix-* names
new Webhook(secret).verify(rawBody, {
'webhook-id': headers[`${p}-id`],
'webhook-timestamp': headers[`${p}-timestamp`],
'webhook-signature': headers[`${p}-signature`],
}, { jsonParse: false }); // throws on a bad signature or stale timestamp → respond 401
}
The library always enforces a fixed 5-minute timestamp window. Hookdeck retries and replays resend the original webhook-timestamp, so the library rejects them once they are more than 5 minutes old. If you receive through Hookdeck and need late retries to pass, use the manual verifier in references/verification.md, which can drop or widen the window. In Express, read the body with express.raw({ type: '*/*' }) so any content type verifies, and parse JSON only after the check.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Type and Payload
Every body is the same envelope, and the event name is the top-level type:
{
"type": "chargeback.created",
"timestamp": "2022-03-15T09:39:24.514Z",
"data": { "id": "04318fb0-e4ff-4712-972e-4db99b3fb23c", "chargebackId": "QRZ54SXB4K6TF232", "psp": "stripe", "status": "waiting_for_data" }
}
timestamp is when the event occurred (ISO 8601). data is the event-specific object. The signature page's code sample uses a ping body with event_type instead; that is a sample, not one of the reference payloads, so route on type.
Common Event Types
| Event | Fired when |
|---|---|
chargeback.created | A new chargeback is received from a connected PSP |
chargeback.updated | A chargeback is updated |
chargeback.status.updated | A chargeback status update is received |
chargeback.evidence.submitted | Evidence is submitted to the PSP (data.status is succeeded or failed) |
chargeback.accepted | A chargeback acceptance is confirmed by the PSP (data.status is succeeded or failed) |
pre_ | A pre-chargeback alert arrives from CDRN, RDR or Ethoca |
pre_ | The transaction linked to a refunded alert was refunded through the PSP |
pre_ | Processing a refund alert failed |
pre_ | An alert was matched to its transaction (opt-in, enabled by Justt per account) |
subscription.cancel_ | A CRM subscription (for example Chargebee or Recurly) was cancelled after a refund |
subscription.cancel_ | That cancellation failed (data.failureCode, data.failureReason) |
These 11 are the documented events, and the examples route all of them (the list may not be exhaustive: ignore types you don't recognise). See references/overview.md.
Delivery Semantics
Return a 2xx within 15 seconds (the reference spec says 30), then process asynchronously. 4xx responses other than 410, 5xx responses, timeouts and connection failures are retried on an 8-attempt schedule (immediately, then after 5 s, 5 min, 30 min, 2 h, 5 h, 10 h and 10 h, about 27.6 hours in all). 410 Gone is permanent and is not retried. An endpoint that fails for 5 consecutive days is disabled. Deduplicate on webhook-id. Return 400 for missing headers and 401 for a bad signature; handlers fail closed (500) when JUSTT_WEBHOOK_SECRET is unset.
Environment Variables
JUSTT_WEBHOOK_SECRET=whsec_xxxxx # full "Signing Secret" from the endpoint's settings; handlers fail closed without it
Each endpoint has its own secret. The docs never print the prefix; the sample base64-decodes the part after _, which matches whsec_<base64>.
Local Development
# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 justt --path /webhooks/justt
Set the printed URL as the endpoint URL in the Justt webhooks dashboard, then use the endpoint's Testing tab to send a signed example.
Reference Materials
- references/overview.md - Event catalog, envelope and payload fields
- references/setup.md - Add an endpoint, find the secret, test, retries
- references/verification.md - Library and manual verification, gotchas, debugging