Webhooks
Webhooks let you receive real-time HTTP notifications when shopping list items change. When an event occurs, Bring Bananas sends a POST request to your configured URL with the event payload.
Events
| Event | Trigger |
|---|---|
item.created | An item is added to a shopping list |
item.updated | An item is modified (name, note, or completion status) |
item.deleted | An item is removed from a shopping list |
Events fire regardless of how the change was made — through the app, the API, or sync.
Payload Format
{
"event": "item.created",
"timestamp": "2026-02-27T14:30:00Z",
"data": {
"item_id": "a1b2c3d4-...",
"shopping_list_id": "e5f6a7b8-...",
"name": "Milk",
"note": "2% fat",
"completed_at": null
}
} Headers
Each delivery includes these headers:
| Header | Description |
|---|---|
Content-Type | application/json |
X-Webhook-Event | The event type (e.g. item.created) |
X-Webhook-Signature | HMAC-SHA256 hex digest of the request body, signed with your secret |
Verifying Signatures
To verify a webhook is authentic, compute an HMAC-SHA256 of the raw request body using your secret and compare it to the X-Webhook-Signature header.
import hmac, hashlib
def verify(body: bytes, secret: str, signature: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature) Retries
If your endpoint returns a non-2xx status code or is unreachable, Bring Bananas retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | 30 seconds |
| 2 | 2 minutes |
| 3 | 8 minutes |
| 4 | 32 minutes |
| 5 | ~2 hours |
After 5 failed attempts the delivery is marked as failed and not retried.
Setup
You can manage webhooks from the Bring Bananas app under Settings. When creating a webhook you'll provide a URL, choose which events to subscribe to, and set a secret (minimum 16 characters). Save the secret — it's only shown once and is used to verify delivery signatures.