Receive real-time CardMind events pushed directly to your server. Webhooks fire whenever a tracked signal triggers, a price threshold is crossed, or your collection value changes significantly. Requires a Premium subscription.
403 PREMIUM_REQUIREDSend a GET to /api/webhooks to retrieve all webhook registrations for your account.
curl "https://api.cardmind.app/api/webhooks" \
-H "Authorization: Bearer cm_live_YOUR_KEY"{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"url": "https://your-server.com/webhooks/cardmind",
"events": ["signal.fired", "price.spike"],
"is_active": true,
"consecutive_failures": 0,
"last_delivered_at": null,
"created_at": "2026-05-29T12:00:00Z"
}
],
"error": null
}Send a POST to /api/webhooks with the destination URL and the event types you want to subscribe to. Maximum 5 active webhooks per account.
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | required | Your HTTPS endpoint that will receive webhook POST requests |
| events | string[] | required | Array of event types to subscribe to (see Event Types below) |
curl -X POST "https://api.cardmind.app/api/webhooks" \
-H "Authorization: Bearer cm_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/cardmind",
"events": ["signal.fired", "price.spike", "price.drop"]
}'{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"url": "https://your-server.com/webhooks/cardmind",
"events": ["signal.fired", "price.spike", "price.drop"],
"is_active": true,
"created_at": "2026-05-29T12:00:00Z"
},
"error": null
}Send a DELETE to /api/webhooks with the webhook id as either a query param or in the JSON body.
curl -X DELETE "https://api.cardmind.app/api/webhooks?id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-H "Authorization: Bearer cm_live_YOUR_KEY"{
"data": { "deleted": true },
"error": null
}| Event | Fires when… |
|---|---|
| signal.fired | A market signal is generated for any card |
| price.spike | A card price increases significantly |
| price.drop | A card price decreases significantly |
| buylist.change | A buylist price changes for a tracked card |
CardMind delivers a POST request to your endpoint with a JSON body. Every payload shares the same top-level shape regardless of event type.
{
"event": "signal.fired",
"data": {
"card_scryfall_id": "e3285e6b-3e79-4d7c-bf96-d920f973b122",
"card_name": "Lightning Bolt",
"signal_type": "price_spike",
"severity": "high",
"detail": "Price increased 34% over 7 days",
"detected_at": "2026-05-29T08:00:00Z"
},
"timestamp": "2026-05-29T08:05:00Z",
"signature": "sha256=a1b2c3d4e5f6..."
}| Field | Type | Description |
|---|---|---|
| event | string | The event type that triggered this delivery (e.g. "signal.fired") |
| data | object | Event-specific payload. Shape varies by event type (see Event Types table) |
| timestamp | string (ISO 8601) | UTC timestamp of when the event was generated on the CardMind server |
| signature | string | HMAC-SHA256 signature of the raw request body, prefixed with "sha256=" |
Webhook deliveries will include a signature field computed as HMAC-SHA256(raw_body, signing_secret). Signing secret distribution is not yet implemented — the verification examples below show the intended pattern for when it is available.
import hmac
import hashlib
def verify_webhook(raw_body: bytes, signature: str, signing_secret: str) -> bool:
expected = "sha256=" + hmac.new(
signing_secret.encode(),
raw_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)import crypto from 'crypto'
function verifyWebhook(rawBody, signature, signingSecret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', signingSecret)
.update(rawBody)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
)
}CardMind retries failed webhook deliveries with exponential backoff. A delivery is considered successful if your endpoint returns any 2xx HTTP status code within 10 seconds.
| Attempt | Delay after previous failure |
|---|---|
| 1 (initial) | Immediate |
| 2 | 30 seconds |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 (final) | 2 hours |
After 5 failed attempts the webhook registration is marked inactive. Delete the registration and create a new one via DELETE /api/webhooks and POST /api/webhooks.