Webhooks
Asynchronous generation endpoints can deliver the finished result to your server instead of making you poll. Pass webhook_url with your request and Mindrift sends a signed JSON POST to that URL once the generation reaches a terminal state (completed or failed).
Subscribing
Add webhook_url (public https URL, default port 443, no redirects) to either endpoint:
POST /v1/text-to-image/subscribe/{model}: returns200with the task descriptor as usual.POST /v1/images/generations: returns202 Acceptedimmediately instead of waiting:
Use the returned id as generation_id to correlate the delivery (or keep polling GET /v1/tasks/{task_id} as a fallback).
What Mindrift delivers
The body mirrors the synchronous response, plus the task envelope: the same handler can process both:
cost is the total billed for the generation in USD (0 when nothing was billed, e.g. refused prompt).
expires_at tells you when the hosted files disappear (null = kept indefinitely). When it is set, archive the file before that date — after expiry the URLs return 404. See Image Retention.
Failed generations are delivered too, with status: "failed" and error: { "code": "moderation_blocked", "message": "..." }, so a refused prompt never leaves you hanging.
Request headers
Verifying the signature
- Fetch the public keys with
GET https://api.mindrift.art/v1/.well-known/jwks.json. Each key’sxfield is the 32-byte Ed25519 public key, base64url-encoded with no padding. Cache the set; refresh it if verification fails (key rotation). - Rebuild the signed message by joining these four values with single
\nnewlines, UTF-8 encoded:{generation-id},{timestamp},{sha256_hex(raw request body bytes)}. Hash the raw body bytes exactly as received (never parse and re-serialize first). - Verify the hex signature against each published key. Accept on the first success, reject otherwise.
Retries, idempotency, and polling
- Return
2xxto acknowledge. Anything else (or a timeout after 10s) is retried with backoff (roughly1m → 5m → 30m → 2h → 8h), then dropped. - Make your handler idempotent: the same
generation_idcan arrive more than once. Treat an already-processed id as a no-op and still return2xx. - Delivery is best-effort: if your endpoint stays down, the delivery is eventually dropped. Keep polling
GET /v1/tasks/{task_id}as the source of truth.
