HTTP API
Complete HTTP API reference for Herald notification endpoints.
Herald exposes a RESTful HTTP API through the Forge framework. When using the Forge extension, all routes are mounted under a configurable base path (default: /herald). The API supports providers, templates, template versions, sending notifications, delivery log, user inbox, preferences, and scoped configuration.
All endpoints accept and return JSON. TypeID strings are used as entity identifiers (e.g., hpvd_01h... for providers).
Send
Send Notification
Sends a notification on a single channel.
POST /herald/sendRequest body:
{
"app_id": "myapp",
"channel": "email",
"template": "welcome",
"locale": "en",
"to": ["alice@example.com"],
"data": {
"name": "Alice",
"app_name": "My App"
},
"org_id": "org-acme",
"user_id": "user-alice",
"metadata": {
"source": "signup"
},
"async": false
}| Field | Type | Required | Description |
|---|---|---|---|
app_id | string | Yes | Application identifier |
channel | string | Yes | Channel type: email, sms, push, inapp |
to | string[] | Yes | Recipient addresses |
template | string | No | Template slug (omit to use subject/body directly) |
locale | string | No | Locale code for template version (defaults to config) |
data | object | No | Template variable data |
subject | string | No | Direct subject (when not using a template) |
body | string | No | Direct body (when not using a template) |
org_id | string | No | Organization ID for scope resolution |
user_id | string | No | User ID for preference checking and scope resolution |
env_id | string | No | Environment ID |
metadata | object | No | User-defined key-value metadata |
async | boolean | No | Whether to send asynchronously |
Response (200):
{
"message_id": "hmsg_01h9xz...",
"status": "sent",
"provider_id": "hpvd_01h9xz...",
"error": ""
}Multi-Channel Notify
Sends a notification across multiple channels using a template.
POST /herald/notifyRequest body:
{
"app_id": "myapp",
"template": "order-confirmation",
"channels": ["email", "inapp"],
"to": ["alice@example.com"],
"user_id": "user-alice",
"data": {
"name": "Alice",
"order_id": "ORD-12345"
}
}| Field | Type | Required | Description |
|---|---|---|---|
app_id | string | Yes | Application identifier |
template | string | Yes | Template slug |
channels | string[] | Yes | Channel types to send on |
to | string[] | Yes | Recipient addresses |
user_id | string | No | User ID for preferences and scope |
locale | string | No | Locale code |
data | object | No | Template variable data |
org_id | string | No | Organization ID |
env_id | string | No | Environment ID |
metadata | object | No | User-defined metadata |
async | boolean | No | Whether to send asynchronously |
Response (200):
[
{
"message_id": "hmsg_01h9xz...",
"status": "sent",
"provider_id": "hpvd_01h9xz..."
},
{
"message_id": "hmsg_01h9y0...",
"status": "sent",
"provider_id": "hpvd_01h9y0..."
}
]Providers
Create Provider
POST /herald/providersRequest body:
{
"app_id": "myapp",
"name": "SMTP Production",
"channel": "email",
"driver": "smtp",
"credentials": {
"host": "smtp.example.com",
"port": "587",
"username": "noreply@example.com",
"password": "secret"
},
"settings": {
"from": "noreply@example.com",
"from_name": "My App"
},
"priority": 0,
"enabled": true
}Response (201):
{
"id": "hpvd_01h9xz...",
"app_id": "myapp",
"name": "SMTP Production",
"channel": "email",
"driver": "smtp",
"credentials": {"host": "smtp.example.com", "port": "587", "username": "noreply@example.com", "password": "secret"},
"settings": {"from": "noreply@example.com", "from_name": "My App"},
"priority": 0,
"enabled": true,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}List Providers
GET /herald/providers?app_id=myapp&channel=email| Parameter | Type | Required | Description |
|---|---|---|---|
app_id | query | Yes | Filter by application |
channel | query | No | Filter by channel type |
Response (200): Array of provider objects.
Get Provider
GET /herald/providers/:idResponse (200): Single provider object.
Update Provider
PUT /herald/providers/:idRequest body (all fields optional):
{
"name": "Updated Name",
"credentials": {"host": "new-smtp.example.com"},
"enabled": false
}Response (200): Updated provider object.
Delete Provider
DELETE /herald/providers/:idResponse: 204 No Content.
Templates
Create Template
POST /herald/templatesRequest body:
{
"app_id": "myapp",
"slug": "welcome",
"name": "Welcome Email",
"channel": "email",
"category": "transactional",
"variables": [
{"name": "name", "type": "string", "required": true},
{"name": "app_name", "type": "string", "required": false, "default": "My App"}
],
"enabled": true
}Response (201): Template object with generated id (htpl_...).
List Templates
GET /herald/templates?app_id=myapp&channel=email| Parameter | Type | Required | Description |
|---|---|---|---|
app_id | query | Yes | Filter by application |
channel | query | No | Filter by channel type |
Response (200): Array of template objects.
Get Template
GET /herald/templates/:idResponse (200): Template object with versions array populated.
Update Template
PUT /herald/templates/:idRequest body (all fields optional):
{
"name": "Updated Welcome Email",
"category": "auth",
"enabled": false
}Response (200): Updated template object.
Delete Template
DELETE /herald/templates/:idDeletes the template and all its associated versions.
Response: 204 No Content.
Template Versions
Create Version
POST /herald/templates/:id/versionsRequest body:
{
"locale": "en",
"subject": "Welcome to {{.app_name}}, {{.name}}!",
"html": "<h1>Hello {{.name}}</h1><p>Welcome aboard!</p>",
"text": "Hello {{.name}}, Welcome aboard!",
"title": "Welcome"
}Response (201): Version object with generated id (htpv_...).
List Versions
GET /herald/templates/:id/versionsResponse (200): Array of version objects for the specified template.
Update Version
PUT /herald/templates/:id/versions/:versionIdRequest body (all fields optional):
{
"subject": "Updated subject line",
"html": "<h1>Updated HTML</h1>",
"active": false
}Response (200): Updated version object.
Delete Version
DELETE /herald/templates/:id/versions/:versionIdResponse: 204 No Content.
Messages
List Messages
GET /herald/messages?app_id=myapp&status=sent&channel=email&offset=0&limit=50| Parameter | Type | Required | Description |
|---|---|---|---|
app_id | query | Yes | Filter by application |
channel | query | No | Filter by channel |
status | query | No | Filter by status: queued, sending, sent, failed, bounced, delivered |
offset | query | No | Pagination offset (default: 0) |
limit | query | No | Page size (default: 50) |
Response (200): Array of message objects.
[
{
"id": "hmsg_01h9xz...",
"app_id": "myapp",
"template_id": "welcome",
"provider_id": "hpvd_01h9xz...",
"channel": "email",
"recipient": "alice@example.com",
"subject": "Welcome to My App, Alice!",
"body": "Hello Alice, Welcome aboard!",
"status": "sent",
"error": "",
"metadata": {"source": "signup"},
"attempts": 1,
"sent_at": "2024-01-15T10:30:01Z",
"created_at": "2024-01-15T10:30:00Z"
}
]Get Message
GET /herald/messages/:idResponse (200): Single message object.
Inbox
List Notifications
GET /herald/inbox?app_id=myapp&user_id=user-alice&offset=0&limit=50| Parameter | Type | Required | Description |
|---|---|---|---|
app_id | query | Yes | Application ID |
user_id | query | Yes | User ID |
offset | query | No | Pagination offset (default: 0) |
limit | query | No | Page size (default: 50) |
Response (200): Array of notification objects.
[
{
"id": "hinb_01h9xz...",
"app_id": "myapp",
"user_id": "user-alice",
"type": "welcome",
"title": "Welcome aboard!",
"body": "Thanks for signing up.",
"action_url": "/getting-started",
"read": false,
"created_at": "2024-01-15T10:30:00Z"
}
]Unread Count
GET /herald/inbox/unread/count?app_id=myapp&user_id=user-aliceResponse (200):
{
"count": 3
}Mark Read
PUT /herald/inbox/:id/readMarks a single notification as read.
Response: 204 No Content.
Mark All Read
PUT /herald/inbox/read-all?app_id=myapp&user_id=user-aliceMarks all unread notifications for the user as read.
Response: 204 No Content.
Delete Notification
DELETE /herald/inbox/:idResponse: 204 No Content.
Preferences
Get Preferences
GET /herald/preferences?app_id=myapp&user_id=user-aliceResponse (200):
{
"id": "hprf_01h9xz...",
"app_id": "myapp",
"user_id": "user-alice",
"overrides": {
"marketing-weekly": {
"email": false,
"push": true
},
"product-updates": {
"email": true,
"push": true,
"sms": false
}
},
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}Update Preferences
PUT /herald/preferencesRequest body:
{
"app_id": "myapp",
"user_id": "user-alice",
"overrides": {
"marketing-weekly": {
"email": false,
"push": true
}
}
}The overrides map keys are template slugs. Each value is a ChannelPreference object with optional boolean fields for email, sms, push, and inapp. A false value means the user opts out of that channel for that template.
Response (200): Updated preference object.
Scoped Configuration
List Configs
GET /herald/config?app_id=myappReturns all scoped configurations (app, org, user) for an application.
Response (200):
[
{
"id": "hscf_01h9xz...",
"app_id": "myapp",
"scope": "app",
"scope_id": "myapp",
"email_provider_id": "hpvd_01h9xz...",
"from_email": "noreply@myapp.com",
"from_name": "My App",
"default_locale": "en",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
},
{
"id": "hscf_01h9y0...",
"app_id": "myapp",
"scope": "org",
"scope_id": "org-acme",
"email_provider_id": "hpvd_01h9y0...",
"from_email": "noreply@acme.com",
"from_name": "Acme Corp"
}
]Set App Config
PUT /herald/config/appRequest body:
{
"app_id": "myapp",
"email_provider_id": "hpvd_01h9xz...",
"sms_provider_id": "hpvd_01h9y0...",
"push_provider_id": "",
"from_email": "noreply@myapp.com",
"from_name": "My App",
"from_phone": "+15551234567",
"default_locale": "en"
}Response (200): Scoped config object.
Set Org Config
PUT /herald/config/org/:orgIdRequest body:
{
"app_id": "myapp",
"email_provider_id": "hpvd_01h9y0...",
"from_email": "noreply@acme.com",
"from_name": "Acme Corp"
}All provider ID and sender fields are optional -- only set fields are applied.
Response (200): Scoped config object.
Set User Config
PUT /herald/config/user/:userIdRequest body:
{
"app_id": "myapp",
"from_name": "Personal Sender",
"default_locale": "fr"
}Response (200): Scoped config object.
Delete Org Config
DELETE /herald/config/org/:orgId?app_id=myappResponse: 204 No Content.
Delete User Config
DELETE /herald/config/user/:userId?app_id=myappResponse: 204 No Content.
Error Responses
All error responses follow the Forge framework's standard error format:
{
"error": {
"code": 400,
"message": "invalid provider ID"
}
}Common Error Codes
| Status | Meaning |
|---|---|
| 400 | Bad request -- invalid ID format or missing required fields |
| 404 | Entity not found |
| 409 | Conflict -- duplicate slug/channel/locale combination |
| 500 | Internal server error |
Route Summary
| Method | Path | Operation |
|---|---|---|
POST | /herald/send | Send single notification |
POST | /herald/notify | Multi-channel notify |
POST | /herald/providers | Create provider |
GET | /herald/providers | List providers |
GET | /herald/providers/:id | Get provider |
PUT | /herald/providers/:id | Update provider |
DELETE | /herald/providers/:id | Delete provider |
POST | /herald/templates | Create template |
GET | /herald/templates | List templates |
GET | /herald/templates/:id | Get template |
PUT | /herald/templates/:id | Update template |
DELETE | /herald/templates/:id | Delete template |
POST | /herald/templates/:id/versions | Create version |
GET | /herald/templates/:id/versions | List versions |
PUT | /herald/templates/:id/versions/:versionId | Update version |
DELETE | /herald/templates/:id/versions/:versionId | Delete version |
GET | /herald/messages | List messages |
GET | /herald/messages/:id | Get message |
GET | /herald/inbox | List inbox |
GET | /herald/inbox/unread/count | Unread count |
PUT | /herald/inbox/:id/read | Mark read |
PUT | /herald/inbox/read-all | Mark all read |
DELETE | /herald/inbox/:id | Delete notification |
GET | /herald/preferences | Get preferences |
PUT | /herald/preferences | Update preferences |
GET | /herald/config | List configs |
PUT | /herald/config/app | Set app config |
PUT | /herald/config/org/:orgId | Set org config |
PUT | /herald/config/user/:userId | Set user config |
DELETE | /herald/config/org/:orgId | Delete org config |
DELETE | /herald/config/user/:userId | Delete user config |