Skip to content
Go To Dashboard

Messaging

Send asynchronous messages, enqueue jobs, and batch deliver payloads to any HTTP endpoint — no Upstash account, no queue infrastructure, just API calls.

import { createFetch } from "@sapiom/fetch";
const sapiomFetch = createFetch({
apiKey: process.env.SAPIOM_API_KEY,
agentName: "my-agent",
});
// Publish a message to a webhook
const response = await sapiomFetch(
"https://upstash.services.sapiom.ai/v1/qstash/publish/https://example.com/webhook",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
type: "job.created",
payload: { id: "job_123" },
}),
}
);
const data = await response.json();
console.log("Message ID:", data.messageId);

Sapiom routes messaging requests through Upstash QStash, a serverless message queue and delivery service. The SDK handles payment negotiation so you don’t need a separate Upstash account.

There are three delivery modes:

  1. Publish — Fire-and-forget delivery to any HTTP endpoint. QStash handles retries automatically.
  2. Enqueue — Ordered delivery through a named queue. Messages in the same queue are processed sequentially.
  3. Batch — Send multiple messages in a single request. Each message can target a different destination or queue.

The destination URL receives your message body as an HTTP POST with the headers you specified.

Powered by Upstash QStash. QStash provides serverless messaging with automatic retries, at-least-once delivery, and dead letter queues.

Base URL: https://upstash.services.sapiom.ai

MethodPathDescription
POST/v1/qstash/publish/*destinationPublish a message to a URL
POST/v1/qstash/enqueue/:queueName/*destinationEnqueue a message to a named queue
POST/v1/qstash/batchSend multiple messages in one request

Endpoint: POST https://upstash.services.sapiom.ai/v1/qstash/publish/{destination}

Send a message to any HTTP endpoint. The destination URL is appended to the path.

// The destination URL is part of the request path
await client.post(
"https://upstash.services.sapiom.ai/v1/qstash/publish/https://example.com/webhook",
{ event: "user.signup", userId: "usr_123" },
{ headers: { "Content-Type": "application/json" } }
);

The message body can be any content type — JSON, plain text, binary, etc. It is forwarded as-is to the destination.

  • Destination must start with http:// or https://
  • Path traversal patterns are rejected
  • Callback, cron, and flow-control headers are stripped before forwarding

Endpoint: POST https://upstash.services.sapiom.ai/v1/qstash/enqueue/{queueName}/{destination}

Add a message to a named queue for ordered processing. Messages in the same queue are delivered sequentially.

// Enqueue to the "email-queue" queue
await client.post(
"https://upstash.services.sapiom.ai/v1/qstash/enqueue/email-queue/https://example.com/send-email",
{ to: "[email protected]", subject: "Welcome!" },
{ headers: { "Content-Type": "application/json" } }
);

Queue names are automatically scoped to your account — no collisions with other users.


Endpoint: POST https://upstash.services.sapiom.ai/v1/qstash/batch

Send multiple messages in a single request. The body must be a non-empty JSON array.

[
{
"destination": "https://example.com/webhook-a",
"headers": { "Content-Type": "application/json" },
"body": "{\"event\":\"order.created\",\"orderId\":\"ord_1\"}"
},
{
"destination": "https://example.com/webhook-b",
"headers": { "Content-Type": "application/json" },
"body": "{\"event\":\"notification.send\",\"userId\":\"usr_1\"}",
"queue": "notifications"
}
]

Each item in the array can target a different destination and optionally specify a queue for ordered delivery.


Endpoint: GET https://upstash.services.sapiom.ai/v1/qstash/messages/{messageId}

Check the delivery status of a published or enqueued message.

const { data } = await client.get(
`${baseUrl}/messages/${messageId}`
);
console.log(data.state); // "delivered", "pending", "failed", "retry"

Endpoint: DELETE https://upstash.services.sapiom.ai/v1/qstash/messages/{messageId}

Cancel a message that hasn’t been delivered yet.


Manage named queues used with the enqueue endpoint.

MethodPathDescription
GET/v1/qstash/queuesList all queues
GET/v1/qstash/queues/:nameGet queue details
DELETE/v1/qstash/queues/:nameDelete queue and pending messages
POST/v1/qstash/queues/:name/pausePause message delivery
POST/v1/qstash/queues/:name/resumeResume message delivery

Queue names are automatically scoped to your account — no collisions with other users.

// List your queues
const { data: queues } = await client.get(`${baseUrl}/queues`);
// Pause a queue
await client.post(`${baseUrl}/queues/email-queue/pause`);
// Resume it later
await client.post(`${baseUrl}/queues/email-queue/resume`);

Create recurring message deliveries using cron expressions.

Endpoint: POST https://upstash.services.sapiom.ai/v1/qstash/schedules/{destination}

The destination URL is part of the path (same pattern as publish). The cron expression is passed via the Upstash-Cron header.

await client.post(
`${baseUrl}/schedules/https://api.example.com/daily-report`,
{ type: "generate-report" },
{
headers: {
"Content-Type": "application/json",
"Upstash-Cron": "0 9 * * *", // Every day at 9 AM
},
}
);
MethodPathDescription
POST/v1/qstash/schedules/*destinationCreate schedule (requires Upstash-Cron header)
GET/v1/qstash/schedulesList schedules
GET/v1/qstash/schedules/:idGet schedule details
DELETE/v1/qstash/schedules/:idDelete schedule
PATCH/v1/qstash/schedules/:id/pausePause schedule
PATCH/v1/qstash/schedules/:id/resumeResume schedule

Standard 5-field cron expressions:

┌─── minute (0-59)
│ ┌─── hour (0-23)
│ │ ┌─── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─── day of week (0-6, Sunday=0)
* * * * *
ExampleDescription
* * * * *Every minute
0 * * * *Every hour
0 9 * * *Daily at 9 AM
0 9 * * 1-5Weekdays at 9 AM
0 0 1 * *First of every month

CodeDescription
400Invalid request — destination must be a valid URL, batch body must be a non-empty array
402Payment required — ensure you’re using the Sapiom SDK
429Rate limit exceeded
import { createFetch } from "@sapiom/fetch";
const sapiomFetch = createFetch({
apiKey: process.env.SAPIOM_API_KEY,
agentName: "my-agent",
});
const baseUrl = "https://upstash.services.sapiom.ai/v1/qstash";
async function processOrder(orderId: string) {
// Fire-and-forget: notify the fulfillment service
await sapiomFetch(
`${baseUrl}/publish/https://api.example.com/fulfillment`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ orderId, action: "ship" }),
}
);
// Ordered queue: send confirmation emails sequentially
await sapiomFetch(
`${baseUrl}/enqueue/email-queue/https://api.example.com/send-email`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ orderId, template: "order-confirmation" }),
}
);
// Batch: notify multiple services at once
await sapiomFetch(`${baseUrl}/batch`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify([
{
destination: "https://api.example.com/analytics",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ event: "order.completed", orderId }),
},
{
destination: "https://api.example.com/inventory",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action: "decrement", orderId }),
},
]),
});
console.log(`Order ${orderId} processing dispatched`);
}
await processOrder("ord_456");
OperationCost
Publish (per message)$0.00001
Enqueue (per message)$0.00001
Batch (per message in array)$0.00001
Schedule (per trigger)$0.00001
Management (get, list, delete, pause, resume)$0.001