Queue Workers

The bext SDK queue is an at-least-once push queue. You enqueue payloads (see SDK → Queue) and register a push-worker: an HTTP handler the dispatcher POSTs each message to. It is reached over the loopback SDK at http://127.0.0.1/__bext/sdk/queue/*, authenticated by the X-Bext-App-Id header (see SDK Overview).

Registering a worker

POST http://127.0.0.1/__bext/sdk/queue/worker/register
X-Bext-App-Id: <app-id>
Content-Type: application/json

Body fields:

Field Required Notes
queue yes Queue name to consume
handler_url yes Your app's public vhost URL for this queue
concurrency no Max in-flight deliveries
visibility_timeout_secs no How long a delivery is held before it's retried
max_attempts no Attempts before a message is dead-lettered

Register handler_url as the app's public URL, not a loopback path — the dispatcher calls it like any HTTP client:

await fetch("http://127.0.0.1/__bext/sdk/queue/worker/register", {
  method: "POST",
  headers: { "X-Bext-App-Id": "<app-id>", "content-type": "application/json" },
  body: JSON.stringify({
    queue: "emails",
    handler_url: "https://<app>.inklura.fr/api/queue/emails",
    concurrency: 4,
    max_attempts: 5,
  }),
});

The handler contract

On each delivery the dispatcher POSTs a JSON envelope to your handler_url:

{ "id": "<message-id>", "queue": "emails", "payload": { }, "attempts": 1 }

Your handler's HTTP status decides the outcome:

Response status Outcome
2xx Ack — the message is done and removed
408, 429, 5xx Retry — redelivered later (until max_attempts)
any other 4xx Dead-letter — moved to the dead-letter queue, not retried
// src/app/api/queue/emails/route.ts
export async function POST(req: Request) {
  const { id, payload, attempts } = await req.json();
  try {
    await sendEmail(payload);
    return new Response("ok");                 // 2xx → ack
  } catch (e) {
    if (isTransient(e)) return new Response("retry", { status: 503 }); // 5xx → retry
    return new Response("bad payload", { status: 400 });               // 4xx → dead-letter
  }
}
Warning

Delivery is at-least-once. A message can arrive more than once (a 2xx that never reached the dispatcher, a redelivery after a visibility timeout). Make handlers idempotent — key side effects on the message id.

No result storage — layer a durable job record

The queue is fire-and-forget with no result storage. Once a message is acked it's gone; there is no built-in place to read "did job X succeed, what did it return." If you need status or a result, write your own durable record — the idiomatic place is KV under a job:<id> key.

// on enqueue
await sdk.kv.set(`job:${id}`, { status: "queued", createdAt: Date.now() });

// in the handler
await sdk.kv.set(`job:${id}`, { status: "running" });
// …work…
await sdk.kv.set(`job:${id}`, { status: "done", result });

Dead-letter operations

Messages that exhaust max_attempts or return a non-retryable 4xx land in the dead-letter queue. Manage them over the SDK:

Method Path Purpose
POST /__bext/sdk/queue/dead/list List dead-lettered messages
POST /__bext/sdk/queue/dead/retry Re-enqueue dead-lettered messages
POST /__bext/sdk/queue/dead/purge Drop dead-lettered messages

Related: company-manager BullMQ queues

The queue described here is the bext SDK queue that a PRISM app owns. Separately, the company-manager Next.js backend runs its own BullMQ (Redis) queues declared in a QUEUE_REGISTRYwebhook, webhook-retry, email-*, seo-*, social-*, woocommerce, prestashop, and the workflow-* automation queues. Those are platform-internal and not registered through the SDK; see Events Overview and Inbound Webhooks (inbound events enqueue onto the webhook queue).

See also