Skip to content

Sendings

The Sending resource is Hantera’s centralized communication queue system. It tracks queued messages (emails, and in the future: SMS, push notifications) through the entire delivery lifecycle with granular status tracking and automatic retries.

What is a Sending?

A Sending represents a single queued communication to a primary recipient. When you send an email using the sendEmail function, a Sending record is created to track delivery status, retries, and any errors that occur.

Key characteristics:

  • One record per primary recipient: Multi-recipient emails create one record for the to recipient; CC/BCC are best-effort delivery
  • Asynchronous processing: Messages are queued and processed in the background
  • Status tracking: Monitor pending, sent, and bounced messages via the Graph API
  • Rate limiting: Respects configured rate limits to avoid overwhelming mail servers
  • Automatic retries: Transient failures are automatically retried with exponential backoff (up to 3 times)

Creating Sendings

Sendings can be created in two ways depending on your use case:

Via Filtrera (Components/Reactors)

Use the sendEmail function in Filtrera for component and reactor logic:

import 'resources'
from sendEmail {
subject = 'Order Confirmation'
body = {
plainText = 'Thank you for your order!'
html = '<h1>Thank you for your order!</h1>'
}
category = 'order_confirmation'
dynamic = {
orderId = order.id
}
}

See: sendEmail function documentation

Via REST API (External Applications)

For external applications, use the HTTP API:

Terminal window
POST /resources/sendings/email
{
"subject": "Order Confirmation",
"body": {
"plainText": "Thank you for your order!",
"html": "<h1>Thank you for your order!</h1>"
},
"category": "order_confirmation",
"dynamic": {
"orderId": "12345"
}
}

Lifecycle & Status

Each Sending progresses through these states:

pending

The message is queued and waiting to be processed by the background service. Pending sendings can be cancelled via the REST API.

sent

The message was successfully delivered to the mail server. Note that this indicates delivery to the mail server, not necessarily to the recipient’s inbox.

bounced

Delivery failed after all retries were exhausted. Check the errorMessage field for details.

cancelled

The sending was cancelled before being processed. Only pending sendings can be cancelled using the DELETE /resources/sendings/{id} REST endpoint.

Transport Types

The transport field indicates the communication channel:

  • email: Email delivery (currently supported)
  • sms: SMS delivery (planned)
  • push: Push notification (planned)

Currently, only email transport is implemented.

Processing Behavior

How it works:

  1. Queueing: When sendEmail is called, a Sending record is created with pending status
  2. Processing: Background service processes the queue every 10 seconds (configurable)
  3. Rate limiting: Default 60 emails/minute (configurable)
  4. Retries: Failed deliveries are retried up to 3 times with exponential backoff
  5. Final status: After retries, status becomes either sent or bounced

Multi-Recipient Behavior

When you provide cc or bcc lists:

  • One Sending record is created for the primary to recipient (with tracked status)
  • CC and BCC recipients receive the email as best-effort delivery
  • No individual tracking for CC/BCC recipients

The cc and bcc recipients are stored as comma-separated strings in the Sending record’s data field, but do not have their own Sending records or status tracking.

Querying Sendings

Use the Graph API to query Sending records:

[
{
"edge": "sendings",
"filter": "status == 'pending'",
"orderBy": "createdAt desc",
"node": {
"fields": ["sendingId", "recipient", "category", "createdAt"]
}
}
]

See: Sending Graph Node for complete query documentation

Custom Data

Store queryable data in the dynamic field:

import 'resources'
from sendEmail {
to = customer.email
subject = 'Campaign Offer'
body = {
html = '<p>Special offer just for you!</p>'
}
dynamic = {
customerId = customer.id
campaignId = campaign.id
segmentId = segment.id
}
}

Then define custom Graph fields to query this data:

uri: /resources/registry/graph/sending/fields/campaignId
spec:
value:
type: text
source: dynamic->'campaignId'

Now you can query by campaign:

[
{
"edge": "sendings",
"filter": "campaignId == '12345' and status == 'sent'",
"count": true
}
]

Access Control

Creating sendings requires the sendings:write permission:

sendings:write # Create new sendings via sendEmail or REST API

Querying sendings uses the Graph API, which requires Graph-specific permissions:

graph/sending:query # Query sending records
graph/sending:field # Access specific fields

Managing sendings via REST requires:

sendings:write # POST /resources/sendings/email (create)
sendings:read # GET /resources/sendings/{id} (retrieve status)
sendings:write # DELETE /resources/sendings/{id} (cancel)

REST API

The Sending resource includes a REST API for queueing, monitoring, and cancelling email delivery from external applications.

Available Endpoints

  • POST /resources/sendings/email - Queue an email for delivery
  • GET /resources/sendings/{id} - Retrieve sending status and details
  • DELETE /resources/sendings/{id} - Cancel a pending sending

When to Use REST vs Filtrera

Use REST API when:

  • Building external integrations
  • Sending emails from non-Hantera environments
  • Need direct HTTP access without Filtrera runtime

Use Filtrera sendEmail when:

  • Writing automations using rules and jobs within Hantera
  • Building Hantera apps

Examples

For practical examples of using the Sendings REST API in real-world scenarios, see the Customer Registration for E-Commerce guide which demonstrates sending registration confirmations and password reset emails.

Cancelling Sendings

Only sendings with pending status can be cancelled. Attempting to cancel sent, bounced, or already cancelled sendings returns a 409 Conflict error.

Terminal window
DELETE /resources/sendings/{sendingId}

Returns:

  • 204 No Content - Successfully cancelled
  • 404 Not Found - Sending does not exist
  • 409 Conflict - Sending status is not pending

Monitoring & Troubleshooting

Check Queue Depth

Monitor the number of pending sendings:

[
{
"edge": "sendings",
"filter": "status == 'pending'",
"count": true
}
]

Health indicators:

  • Healthy: < 50 pending
  • Warning: 50-100 pending
  • Critical: > 100 pending

Find Recent Failures

[
{
"edge": "sendings",
"filter": "status == 'bounced' and createdAt >= '2025-10-30T00:00:00Z'",
"orderBy": "createdAt desc",
"node": {
"fields": ["sendingId", "recipient", "errorMessage", "retryCount", "category"]
}
}
]

Calculate Bounce Rate

[
{
"edge": "sendings",
"alias": "total",
"filter": "createdAt >= '2025-10-01T00:00:00Z'",
"count": true
},
{
"edge": "sendings",
"alias": "bounced",
"filter": "createdAt >= '2025-10-01T00:00:00Z' and status == 'bounced'",
"count": true
}
]

A bounce rate above 5% may indicate email list quality issues.