Webhooks
このコンテンツはまだ日本語訳がありません。
Webhooks let your server receive HTTP callbacks when a ZIP job finishes. Instead of polling the Jobs API, register an endpoint URL and Eazip will POST the result to you automatically.
Setup in Dashboard
Section titled “Setup in Dashboard”- Log in to your Eazip dashboard
- Navigate to Webhooks in the sidebar
- Click Add Endpoint and enter your HTTPS endpoint URL
- After creation, click the endpoint to open the detail page and copy the Active Secret
You can view your signing secret at any time from the webhook detail page.
Requirements
Section titled “Requirements”- The endpoint URL must use HTTPS.
- Your server must respond within 10 seconds with a
2xxstatus code.
When Webhooks Are Sent
Section titled “When Webhooks Are Sent”A webhook is fired whenever a ZIP job reaches a terminal state:
| Event | Trigger |
|---|---|
job.completed | ZIP file is ready for download |
job.failed | Job failed — all files could not be fetched, or a fail-fast error occurred |
If you have multiple webhook endpoints registered, each one receives its own delivery independently.
Payload
Section titled “Payload”Every webhook delivery is an HTTP POST with Content-Type: application/json. The body has the following structure:
{ "delivery_id": "d4e5f6a7-b8c9-4d2e-a1f3-9c8b7a6e5d4f", "event": "job.completed", "job_id": "550e8400-e29b-41d4-a716-446655440000", "status": "completed", "url_count": 3, "file_count": 3, "errors": null, "download_url": "https://api.eazip.io/download/eyJ...", "metadata": { "order_id": "ord_123" }, "timestamp": "2025-01-21T10:00:45.000Z"}Payload Fields
Section titled “Payload Fields”| Field | Type | Description |
|---|---|---|
delivery_id | string | Unique UUID for this webhook delivery. Use this for idempotency checks — the same delivery_id is sent on retries of the same delivery. |
event | string | "job.completed" or "job.failed" |
job_id | string | UUID of the ZIP job |
status | string | Job status (completed or failed) |
url_count | number | Number of URLs submitted in the job |
file_count | number | null | Number of files actually packaged (null if the job failed before packaging) |
errors | array | null | Array of { url, error } objects for failed file fetches. null when there are no errors. |
download_url | string | null | Signed download URL for the ZIP. null when the job failed. |
metadata | object | null | The key/value pairs you passed when creating the job |
timestamp | string | ISO 8601 timestamp of when the event was generated |
Example: Failed Job
Section titled “Example: Failed Job”{ "delivery_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "event": "job.failed", "job_id": "550e8400-e29b-41d4-a716-446655440000", "status": "failed", "url_count": 2, "file_count": null, "errors": [ { "url": "https://example.com/missing.pdf", "error": "HTTP 404" } ], "download_url": null, "metadata": null, "timestamp": "2025-01-21T10:01:00.000Z"}Verifying Signatures
Section titled “Verifying Signatures”Every delivery includes an X-Webhook-Signature header containing an HMAC-SHA256 signature. Always verify this signature before processing the payload to ensure it was sent by Eazip and has not been tampered with.
How It Works
Section titled “How It Works”- Eazip computes
HMAC-SHA256(request_body, your_signing_secret)and hex-encodes the result. - The hex string is sent in the
X-Webhook-Signatureheader. - Your server computes the same HMAC over the raw request body using your stored secret and compares it.
Verification Examples
Section titled “Verification Examples”import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyWebhook(rawBody, signatureHeader, secret) { const expected = createHmac('sha256', secret) .update(rawBody) .digest('hex');
// During secret rotation, the header may contain two // comma-separated signatures — check each one. const signatures = signatureHeader.split(','); return signatures.some((sig) => { if (sig.length !== expected.length) return false; return timingSafeEqual( Buffer.from(sig, 'utf8'), Buffer.from(expected, 'utf8'), ); });}import hmacimport hashlib
def verify_webhook(raw_body: bytes, signature_header: str, secret: str) -> bool: expected = hmac.new( secret.encode(), raw_body, hashlib.sha256 ).hexdigest()
# During secret rotation, the header may contain two # comma-separated signatures — check each one. signatures = signature_header.split(",") return any(hmac.compare_digest(sig, expected) for sig in signatures)require 'openssl'
def verify_webhook(raw_body, signature_header, secret) expected = OpenSSL::HMAC.hexdigest("SHA256", secret, raw_body)
# During secret rotation, the header may contain two # comma-separated signatures — check each one. signatures = signature_header.split(",") signatures.any? { |sig| OpenSSL.secure_compare(sig, expected) }endimport ( "crypto/hmac" "crypto/sha256" "encoding/hex" "strings")
func verifyWebhook(rawBody []byte, signatureHeader, secret string) bool { mac := hmac.New(sha256.New, []byte(secret)) mac.Write(rawBody) expected := hex.EncodeToString(mac.Sum(nil))
// During secret rotation, the header may contain two // comma-separated signatures — check each one. for _, sig := range strings.Split(signatureHeader, ",") { if hmac.Equal([]byte(sig), []byte(expected)) { return true } } return false}Secret Rotation
Section titled “Secret Rotation”Over time you may need to rotate your signing secret — for example, after a team member leaves or as part of a regular security policy. Eazip supports zero-downtime rotation with a grace period.
How It Works
Section titled “How It Works”- Open the webhook detail page in the dashboard and click Rotate Secret. Choose a grace period (1–24 hours) and click Rotate.
- Eazip generates a new Pending Secret. Both the Active Secret and the new Pending Secret are displayed on the detail page.
- During the grace period, every delivery includes two comma-separated signatures in
X-Webhook-Signature— one computed with the active secret and one with the pending secret. - Update your server to verify against the new secret.
- When the grace period expires, the pending secret automatically promotes to the active secret. From that point, only one signature is sent.
While a rotation is in progress, the Rotate Secret button shows “Rotation in progress” and is disabled. The remaining grace period is displayed as a badge next to Grace Period Remaining.
Timeline
Section titled “Timeline” Rotate called Grace period expires │ │ ▼ ▼ ─────┬────────────────────────────────┬────────── │ Both signatures sent │ New secret only │ (active + pending) │ (pending → active) ─────┴────────────────────────────────┴──────────Only one rotation can be in progress at a time. Attempting to rotate while a pending secret exists returns 409 Conflict.
Retry & Delivery Guarantees
Section titled “Retry & Delivery Guarantees”If your endpoint does not return a 2xx response (or the request times out after 10 seconds), Eazip will automatically retry the delivery.
Retries are processed on a short interval. Up to 5 attempts are made in total. After 5 failed attempts, the delivery is marked as permanently failed and no further retries are attempted.
What Counts as a Failure
Section titled “What Counts as a Failure”- Any HTTP response with a non-
2xxstatus code (e.g.500,503,429) - Connection timeout (no response within 10 seconds)
- Connection error (endpoint unreachable)
Delivery Status
Section titled “Delivery Status”Each delivery has one of these statuses:
| Status | Description |
|---|---|
pending | Delivery in progress or scheduled for retry |
success | Endpoint returned a 2xx response |
failed | All retry attempts exhausted, or endpoint was deactivated |
Viewing Delivery History
Section titled “Viewing Delivery History”Open a webhook’s detail page in the dashboard and select the Delivery History tab. Each delivery shows:
| Column | Description |
|---|---|
| Event | Event type (e.g. job.completed) |
| Status | pending (yellow), success (green), or failed (red) |
| HTTP Status | The HTTP status code returned by your endpoint (blank if no response was received) |
| Attempts | Number of delivery attempts made so far |
| Sent | When the delivery was first created |
The most recent 50 deliveries are shown.
Idempotency
Section titled “Idempotency”Your endpoint may receive the same event more than once (e.g. due to retries after a network issue where the response was lost). Design your handler to be idempotent — use delivery_id as a deduplication key to avoid processing the same delivery twice. The delivery_id stays the same across retries of the same delivery. You can also use job_id if you only need one handler invocation per job regardless of how many endpoints you have registered.
Best Practices
Section titled “Best Practices”- Always verify signatures — never trust incoming webhooks without checking the HMAC signature.
- Respond quickly — return a
200immediately and process the payload asynchronously. If your handler takes too long, the delivery will time out and be retried. - Handle duplicates — use
delivery_idto deduplicate deliveries in case of retries (orjob_idto deduplicate per job). - Use HTTPS — webhook URLs must use HTTPS. This ensures the payload (including
download_url) is encrypted in transit. - Rotate secrets regularly — use the grace period mechanism to rotate secrets without downtime.
- Monitor deliveries — check delivery history to catch persistent failures early.