Skip to content

Events, webhooks, and jobs

Savorum tells you about changes in two ways: a live event stream for connected clients, and webhooks for services. Large or slow work runs as a job you can check on later. This page covers how to use them together. The operation pages hold the exact limits and fields.

Follow library changes streams events for the library in X-Savorum-Library. Each event names what changed and its new revision. It never carries recipe text or credentials.

Treat an event as a prompt to refresh, not as the data itself. When you get a resync event, you missed changes that can’t be replayed: reload everything you depend on.

The server closes each connection after five minutes. Reconnect with backoff and send the last event ID in Last-Event-ID to pick up where you left off.

A webhook sends the same events to your HTTPS endpoint. Library owners create one with create library webhook. A personal API token can’t manage webhooks, so sign in to a browser session to set one up.

Each delivery is signed. In your receiver:

  1. Verify Savorum-Signature against the exact bytes you received, before parsing them.
  2. Reject a timestamp more than five minutes from now.
  3. Record the delivery ID and ignore one you have seen. Savorum delivers at least once, so duplicates happen.

The signing secret appears only once, when you create the webhook or rotate its secret. If you lose that response, rotate the secret and use the new one.

After repeated failures, Savorum pauses the webhook. It keeps the queued deliveries but stops adding new ones. Fix the receiver, then turn the webhook back on and resend what you need from its delivery history.

Bulk changes, exports, and automated imports can run as jobs. A job belongs to the account that started it.

  • Apply bulk changes returns 202 Accepted with a job when the list is over the synchronous limit, or when you send an Idempotency-Key. Otherwise it answers at once. Some items can succeed while others fail, so check each result.
  • Queue an export and queue an import preview always run as jobs.

Check progress with read a bulk or transfer job and per-item results with read job item results. Jobs and their downloads expire 24 hours after the job is created.

If a job is interrupted, resume it. An item reported as outcome-unknown may or may not have changed, and Savorum doesn’t repeat it. Read that resource before acting on it again.

An import only commits on its own when you set policy to require-no-warnings or allow-warnings. Otherwise it waits at a preview until you commit it. Updating an existing recipe from an import needs the review step in the web app.

A connection can drop after the server saved your change but before you got the answer. To retry a creation without making a duplicate, send an Idempotency-Key. Operations that accept one list it as a parameter.

  1. Before the first attempt, save the key, the request, and the time you first sent it.
  2. After an unclear failure, send exactly the same request with the same key. You get back the original resource, or the original job, and nothing happens twice.
  3. If you change the request, it is a new submission: use a new key. Reusing a key with different input returns 409 with idempotency-conflict.

Savorum remembers a key for limits.retry_receipt_hours from capabilities, counted from your first attempt. Waiting for a response doesn’t extend it. When that time runs out:

  1. Stop retrying automatically, and keep the unfinished submission.
  2. Read the server state to see whether the creation happened.
  3. Settle any difference before you submit it again as a new request.

If the resource was deleted in the meantime, a retry reports it missing and doesn’t recreate it.

Calendar feed URLs and webhook secrets appear only in the first successful response. A retry returns the resource without them. If you lost the first response, rotate the webhook secret or replace the feed URL.

POST /api/v1/shopping/lists/{list_id}/items adds several items to a shopping list at once. It requires an Idempotency-Key and a stable id for each item:

{
"items": [
{ "id": "milk", "name": "Milk", "buy": "1 carton" },
{ "id": "eggs", "name": "Eggs" }
]
}

Items also accept note and category, and keep their order. The batch is all or nothing: one bad item or ID conflict rejects the whole request. A retry returns the list and the revision your batch applied at, even after later edits. The largest batch is limits.grocery_addition_items.

Other shopping changes, including list renames and applying a reviewed recipe addition, carry an operation ID in the body. If you also send an Idempotency-Key, it must equal that ID. Savorum keeps these for as long as the list exists, so an app can replay changes it queued while offline.