Skip to main content

Messages

Looking for the product view?
The Help Center explains this surface as users see it: Messages.

Apps can send push notifications and emails to users through a durable message queue. Messages are enqueued via the notify() and email() callbacks in server route handlers, webhook handlers, and agent tools, then delivered asynchronously by a background dispatcher with retry and dead-letter handling.

Message Lifecycle

pending → dispatched → delivered (success)
pending → dispatched → pending (failure, will retry)
pending → dispatched → dead (max retries exceeded)
dead/failed → pending (manual retry via API)

Channels

ChannelRecipientDelivery Method
pushInformer usernameFCM to all registered devices (Informer GO)
emailEmail addressTenant mail transport (SMTP, Gmail API, Microsoft Graph)

Push notifications include the app's ID automatically — tapping a notification in Informer GO opens the app, optionally at a specific sub-page via the path field.


GET /api/apps/{id}/messages

List messages for an app with optional filters and cursor-based pagination.

Authentication: Required

Permissions Required: Member+ role (write permission)

Path Parameters:

ParameterTypeDescription
idstringApp UUID or natural ID

Query Parameters:

ParameterTypeDefaultDescription
channelstringFilter by channel: push or email
statusstringFilter by status: pending, dispatched, delivered, failed, dead
recipientstringFilter by recipient (exact match)
limitnumber100Max results (1–500)
beforeISO dateCursor: return messages created before this timestamp
List an App's messagesGET /api/apps/admin:order-desk/messages
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/messages"
}
},
"messages": [
{
"id": "00000000-0000-4000-8000-000000000001",
"channel": "push",
"recipient": "annie.larson",
"payload": {
"body": "Order #1042 left the warehouse",
"path": "/orders/1042",
"title": "Order shipped"
},
"status": "delivered",
"attempts": 1,
"maxAttempts": 3,
"error": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"processedAt": "2026-01-15T16:20:00.000Z"
},
{
"id": "00000000-0000-4000-8000-000000000002",
"channel": "push",
"recipient": "abel.jacobs",
"payload": {
"body": "Chai is below reorder point",
"title": "Low stock"
},
"status": "pending",
"attempts": 0,
"maxAttempts": 3,
"error": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"processedAt": null
},
{
"id": "00000000-0000-4000-8000-000000000003",
"channel": "email",
"recipient": "carol@example.com",
"payload": {
"to": "carol@example.com",
"html": "<p>12 orders today</p>",
"subject": "Daily order summary"
},
"status": "delivered",
"attempts": 1,
"maxAttempts": 3,
"error": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"processedAt": "2026-01-15T16:20:00.000Z"
},
"… 1 more items (4 total) — omitted from docs"
],
"summary": [
{
"status": "delivered",
"count": 2
},
{
"status": "failed",
"count": 1
},
{
"status": "pending",
"count": 1
}
],
"daily": [
{
"day": "2026-01-15",
"channel": "email",
"count": 2
},
{
"day": "2026-01-15",
"channel": "push",
"count": 2
}
]
}
Captured from the API examples test suite; ids and timestamps are normalized.

The summary provides status counts (useful for dashboard indicators) and daily provides per-day, per-channel volume.

Filter by channelGET /api/apps/admin:order-desk/messages?channel=email
channel and status filters combine with recipient, limit, and the before cursor.
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/messages"
}
},
"messages": [
{
"id": "00000000-0000-4000-8000-000000000001",
"channel": "email",
"recipient": "carol@example.com",
"payload": {
"to": "carol@example.com",
"html": "<p>12 orders today</p>",
"subject": "Daily order summary"
},
"status": "delivered",
"attempts": 1,
"maxAttempts": 3,
"error": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"processedAt": "2026-01-15T16:20:00.000Z"
},
{
"id": "00000000-0000-4000-8000-000000000002",
"channel": "email",
"recipient": "dave@example.com",
"payload": {
"to": "dave@example.com",
"html": "<p>1 failure</p>",
"subject": "Webhook failure digest"
},
"status": "failed",
"attempts": 2,
"maxAttempts": 3,
"error": "Connection refused",
"createdAt": "2026-01-15T16:20:00.000Z",
"processedAt": null
}
],
"summary": [
{
"status": "delivered",
"count": 2
},
{
"status": "failed",
"count": 1
},
{
"status": "pending",
"count": 1
}
],
"daily": [
{
"day": "2026-01-15",
"channel": "email",
"count": 2
},
{
"day": "2026-01-15",
"channel": "push",
"count": 2
}
]
}
Captured from the API examples test suite; ids and timestamps are normalized.

Cursor Pagination:

Use the createdAt of the last message as the before parameter:

// First page
const page1 = await fetch('/api/apps/{id}/messages?limit=50');

// Next page
const lastDate = page1.messages[page1.messages.length - 1].createdAt;
const page2 = await fetch(`/api/apps/{id}/messages?limit=50&before=${lastDate}`);

GET /api/apps/{id}/messages/{messageId}

Get a single message by ID.

Authentication: Required

Permissions Required: Member+ role (write permission)

Path Parameters:

ParameterTypeDescription
idstringApp UUID or natural ID
messageIdstringMessage UUID

Response: Single message object (same shape as items in the list response).

Get a single messageGET /api/apps/admin:order-desk/messages/00000000-0000-4000-8000-000000000001
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/messages/00000000-0000-4000-8000-000000000001"
}
},
"id": "00000000-0000-4000-8000-000000000001",
"tenant": "acme",
"appId": "00000000-0000-4000-8000-000000000002",
"channel": "push",
"recipient": "annie.larson",
"payload": {
"body": "Order #1042 left the warehouse",
"path": "/orders/1042",
"title": "Order shipped"
},
"status": "delivered",
"attempts": 1,
"maxAttempts": 3,
"error": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"processedAt": "2026-01-15T16:20:00.000Z"
}
Captured from the API examples test suite; ids and timestamps are normalized.

Error Responses:

  • 404 Not Found — Message does not exist or does not belong to this app

POST /api/apps/{id}/messages/{messageId}/_retry

Retry a failed or dead message. Resets the message to pending status with zero attempts so the dispatcher will pick it up again.

Authentication: Required

Permissions Required: Member+ role (write permission)

Path Parameters:

ParameterTypeDescription
idstringApp UUID or natural ID
messageIdstringMessage UUID

Response: The updated message object with status: "pending".

Retry a failed messagePOST /api/apps/admin:order-desk/messages/00000000-0000-4000-8000-000000000001/_retry
Retry re-queues failed or dead messages; the dispatcher picks them up on its next pass.
Response · 200
{
"_links": {
"self": {
"href": "https://informer.example.com/api/apps/admin%3Aorder-desk/messages/00000000-0000-4000-8000-000000000001/_retry"
}
},
"id": "00000000-0000-4000-8000-000000000001",
"tenant": "acme",
"appId": "00000000-0000-4000-8000-000000000002",
"channel": "email",
"recipient": "dave@example.com",
"payload": {
"to": "dave@example.com",
"html": "<p>1 failure</p>",
"subject": "Webhook failure digest"
},
"status": "pending",
"attempts": 0,
"maxAttempts": 3,
"error": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"processedAt": null
}
Captured from the API examples test suite; ids and timestamps are normalized.

Error Responses:

  • 404 Not Found — Message does not exist
  • 409 Conflict — Message is not in a retryable state (e.g., already pending or delivered)

Sending Messages from App Code

Messages are sent from server route handlers, webhook handlers, and agent tools using the notify() and email() callbacks. See the handler documentation for full API details.

Quick Reference

// Push notification
const { id } = await notify('username', {
title: 'Alert',
body: 'Something happened',
path: '/details/123' // optional deep link
});

// Email
const { id } = await email('user@example.com', {
subject: 'Report Ready',
html: '<p>Your report is attached.</p>',
from: 'noreply@example.com' // optional
});

// Bulk push
const { ids, queued } = await notify([
{ username: 'alice', title: 'Update', body: '...' },
{ username: 'bob', title: 'Update', body: '...' },
]);

// Bulk email
const { ids, queued } = await email([
{ to: 'alice@example.com', subject: 'Update', html: '...' },
{ to: 'bob@example.com', subject: 'Update', html: '...' },
]);

HAL Relations

RelationDescription
inf:app-messagesLink to the app's message list
inf:app-messageLink to a single message
inf:app-message-retryRetry a failed/dead message