Agent skill
Nash Webhooks Skill
Receive and verify Nash (usenash.com, now nash.ai) delivery orchestration webhooks. Nash sends webhooks through Svix, so verify the svix-id, svix-timestamp and svix-signature headers (HMAC-SHA256, base64, over "{svix-id}.{svix-timestamp}.{raw body}" keyed with the endpoint's whsec_ signing secret) with the svix SDK or manually. Use when setting up a Nash webhook handler, debugging Nash signature verification, or handling the { type, event, data } payload for delivery events (dropoff_complete, canceled_by_provider), task events (task.created, task.canceled_by_provider), courier_location.updated, courier_name.updated and shift.dispatched.
Install this skill
npx skills add hookdeck/webhook-skills --skill nash-webhooks
Nash (usenash.com, now nash.ai) is a delivery orchestration platform: one API to quote and dispatch deliveries across courier networks, local fleets and your own drivers. Nash webhooks push every job, task and courier change to your endpoint so you don't poll. Docs: docs.usenash.com/reference/webhooks.
When to Use This Skill
- How do I receive Nash webhooks?
- How do I verify Nash webhook signatures (
svix-signature)? - Where do I find the Nash webhook signing secret?
- How do I handle
task.created,task.canceled_orby_ provider shift.dispatched? - How do I track delivery status (
dropoff_complete,canceled_by_provider) from Nash webhooks? - Why is my Nash webhook signature verification failing?
How Nash Signs Webhooks
Nash delivers through Svix. Every request carries three headers:
| Header | Content |
|---|---|
svix-id | Message ID, identical on every retry (use it to deduplicate) |
svix-timestamp | Unix seconds when the attempt was signed |
svix-signature | Space-delimited list of v1,<base64 signature> entries |
The signature is base64(HMAC-SHA256(key, "{svix-id}.{svix-timestamp}.{raw body}")), where key is the base64-decoded part of the signing secret after whsec_. Get the secret in the Nash Portal: Settings > Webhook Management > click an endpoint > Signing Secret. Nash recommends the Svix SDKs; they also enforce a 5-minute timestamp tolerance.
Verification (core)
// svix 2.x is ESM-only: use import (or `await import('svix')` from CommonJS).
// Python: pip install svix; from svix.webhooks import Webhook
import { Webhook } from 'svix';
// rawBody: the exact request body string/bytes, read BEFORE any JSON parsing.
function verifyNashWebhook(rawBody, headers, secret = process.env.NASH_WEBHOOK_SECRET) {
// Throws on a bad signature or a timestamp more than 5 minutes off.
// svix 2.x returns nothing, so parse the body yourself afterwards.
new Webhook(secret).verify(rawBody, {
'svix-id': headers['svix-id'],
'svix-timestamp': headers['svix-timestamp'],
'svix-signature': headers['svix-signature'],
});
const { type, event, data } = JSON.parse(rawBody);
return { eventType: `${type}.${event}`, data };
}
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
The manual algorithm (no SDK) is in references/verification.md.
Payload and Event Types
Every payload is { "type", "event", "data" }. type is the entity and event the status within it; the examples dispatch on `${type}.${event}`.
type | Example event values | data |
|---|---|---|
delivery | created, assigned_driver, pickup_complete, dropoff_complete, canceled_by_provider, failed | Full Job object |
task | created, canceled_by_provider, canceled_by_reassign, failed, dispatch_failure | Full Job object |
courier_location | updated (every 1-2 minutes) | Full Job object |
courier_name | updated (same provider swapped drivers) | Full Job object |
shift | dispatched | { eventSourceId, created_ |
The examples handle delivery.created, delivery.assigned_driver, delivery.dropoff_, delivery.canceled_, delivery.failed, task.created, task.canceled_, task.failed, courier_location.updated, courier_name.updated and shift.dispatched. The full lists are in references/overview.md.
Things that trip people up:
- Each payload is a snapshot of the whole job, not a diff. A job can hold several tasks (delivery attempts), so the same delivery event can arrive once per task.
- Reassignment creates a new task. The old task emits a terminal event (
task.canceled_,by_ provider task.canceled_, ...) and the new one emitsby_ auto_ reassign task.created. Treat a task cancellation as terminal for that task only. - Timestamps in
dataare UTC ISO 8601 with microseconds and no timezone suffix.
Environment Variables
# Nash Portal > Settings > Webhook Management > endpoint > Signing Secret
NASH_WEBHOOK_SECRET=whsec_your_signing_secret_here
Local Development
npx hookdeck-cli listen 3000 nash --path /webhooks/nash
No account required. The CLI creates a guest account on first run and gives you a public URL to register under Settings > Webhook Management, plus a web UI for inspecting requests. Use port 8000 for the FastAPI example.
Retries
Svix retries failed deliveries (network errors, 5xx, and 408/409/429) immediately, then after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and 10 hours. Return 2xx quickly and do the work asynchronously. Failed messages can be replayed from the Portal (Webhook Management > endpoint > Attempts). Returning 410 Gone disables the endpoint.
Reference Materials
- references/overview.md - Payload envelope, every event type, reassignment behavior
- references/setup.md - Registering an endpoint and getting the signing secret
- references/verification.md - SDK and manual verification, gotchas, debugging