Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Quickstart

In five minutes you'll have a working scope, a queue inside it, one message published, one message claimed, and one message completed. This is the smallest possible useful ScaiQueue interaction.

You need:

  • A ScaiGrid API key with scaiqueue:manage, scaiqueue:publish, and scaiqueue:consume (any tenant admin has all three).
  • curl, Python with httpx, or Node — pick one of the blocks below.
bash
1
2
export SCAIGRID_HOST="https://scaigrid.scailabs.ai"
export SCAIGRID_API_KEY="sgk_..."

1. Create a scope#

A scope is a namespace for related queues. Creating one also auto-creates the system queues (_dead_letter, _events, _audit, _integrity, _corrections).

bash
1
2
3
4
5
6
7
curl -X POST "$SCAIGRID_HOST/v1/modules/scaiqueue/scopes" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "quickstart",
    "display_name": "Quickstart"
  }'
python
1
2
3
4
5
6
7
8
import httpx, os
H = {"Authorization": f"Bearer {os.environ['SCAIGRID_API_KEY']}"}
scope = httpx.post(
    f"{os.environ['SCAIGRID_HOST']}/v1/modules/scaiqueue/scopes",
    headers=H,
    json={"slug": "quickstart", "display_name": "Quickstart"},
).json()["data"]
print(scope["id"])
javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const res = await fetch(`${process.env.SCAIGRID_HOST}/v1/modules/scaiqueue/scopes`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SCAIGRID_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ slug: "quickstart", display_name: "Quickstart" }),
});
const { data: scope } = await res.json();
console.log(scope.id);

Save the returned scope.id as SCOPE_ID.

2. Create a queue#

Queues live inside scopes. Each queue has an ordering mode (fifo, priority, or deadline) and a consumer mode (competing or broadcast). For most work distribution you want competing + fifo.

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curl -X POST "$SCAIGRID_HOST/v1/modules/scaiqueue/scopes/$SCOPE_ID/queues" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "tasks",
    "display_name": "Tasks",
    "consumer_mode": "competing",
    "ordering": "fifo",
    "max_depth": 0
  }'
python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
queue = httpx.post(
    f"{os.environ['SCAIGRID_HOST']}/v1/modules/scaiqueue/scopes/{scope['id']}/queues",
    headers=H,
    json={
        "slug": "tasks",
        "display_name": "Tasks",
        "consumer_mode": "competing",
        "ordering": "fifo",
    },
).json()["data"]
print(queue["id"])
javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const qres = await fetch(
  `${process.env.SCAIGRID_HOST}/v1/modules/scaiqueue/scopes/${scope.id}/queues`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.SCAIGRID_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      slug: "tasks",
      display_name: "Tasks",
      consumer_mode: "competing",
      ordering: "fifo",
    }),
  },
);
const { data: queue } = await qres.json();

Save queue.id as QUEUE_ID.

3. Publish a message#

Publishing writes a row in MariaDB and pushes the new message id into the queue's Redis pending sorted set. Use idempotency_key so retried publishes don't create duplicates. Labels and priority are optional but cheap to set early — routing rules consume both.

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curl -X POST "$SCAIGRID_HOST/v1/modules/scaiqueue/scopes/$SCOPE_ID/queues/$QUEUE_ID/messages" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "task.run",
    "body": {"order_id": "ord_12345"},
    "priority": {"tier": "normal", "score": 500},
    "labels": {"region": "eu"},
    "idempotency_key": "ord_12345"
  }'
python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
msg = httpx.post(
    f"{os.environ['SCAIGRID_HOST']}/v1/modules/scaiqueue/scopes/{scope['id']}/queues/{queue['id']}/messages",
    headers=H,
    json={
        "type": "task.run",
        "body": {"order_id": "ord_12345"},
        "priority": {"tier": "normal", "score": 500},
        "labels": {"region": "eu"},
        "idempotency_key": "ord_12345",
    },
).json()["data"]
print(msg["id"], msg["state"])
javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const pubres = await fetch(
  `${process.env.SCAIGRID_HOST}/v1/modules/scaiqueue/scopes/${scope.id}/queues/${queue.id}/messages`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.SCAIGRID_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      type: "task.run",
      body: { order_id: "ord_12345" },
      priority: { tier: "normal", score: 500 },
      idempotency_key: "ord_12345",
    }),
  },
);
const { data: msg } = await pubres.json();

The same idempotency_key published twice returns the original message id instead of creating a duplicate.

4. Claim a message#

Claim takes the next message off the queue and locks it for you. The lock lasts visibility_timeout_s — if you don't complete or fail the message within that window, the visibility-timeout enforcer puts it back as pending and another consumer can take it.

bash
1
2
3
4
curl -X POST "$SCAIGRID_HOST/v1/modules/scaiqueue/scopes/$SCOPE_ID/queues/$QUEUE_ID/messages/claim" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"batch_size": 1, "visibility_timeout_s": 60}'

The response is a list. Each claimed message includes the full body, the labels, and an attempts counter that ScaiQueue has already incremented. You have visibility_timeout_s seconds to call complete, fail, or extend — if you don't, the visibility-timeout enforcer returns it to pending and another consumer can claim it.

5. Complete the message#

Completion moves the message to its terminal state and frees the slot. Pass any structured result you want downstream consumers to read in the response field — it ends up on the message row and in the event payload.

bash
1
2
3
4
curl -X POST "$SCAIGRID_HOST/v1/modules/scaiqueue/scopes/$SCOPE_ID/messages/$MSG_ID/complete" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"response": {"shipped": true}}'

The message moves to completed, the queue's depth_claimed decrements, and a scaiqueue.message.completed event is published on ScaiGrid's internal event bus carrying the message's correlation_id — so any worker (or ScaiCore checkpoint resolver) waiting on this id can react without polling.

What just happened#

  • The scope you created is a row in MariaDB. Five system queues were created alongside it.
  • Each publish wrote a row in mod_scaiqueue_messages and added the message id to a per-queue Redis sorted set scored by ordering mode (created_at for FIFO, deadline for deadline, tier+score for priority).
  • The claim was a ZPOPMIN on that sorted set plus a SET NX EX claim lock — atomic across consumers.
  • Completion released the Redis claim and decremented queue counters.

Next#

Updated 2026-05-18 15:01:32 View source (.md) rev 12