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)

INFO

System Sendings: The Sending system is used both by apps (through sendEmail) and by Hantera's internal platform features (password resets, email validations, etc.). Platform-generated sendings use categories with the system: prefix (e.g., system:password_reset). This prefix is reserved and cannot be used in app-created sendings.

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 logic:

filtrera
import 'resources'

from sendEmail {
  to = 'customer@example.com'
  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:

bash
POST /resources/sendings/email
json
{
  "to": "customer@example.com",
  "subject": "Order Confirmation",
  "body": {
    "plainText": "Thank you for your order!",
    "html": "<h1>Thank you for your order!</h1>"
  },
  "category": "order_confirmation",
  "dynamic": {
    "orderId": "12345"
  }
}

TIP

See the HTTP API Reference for complete REST API documentation including all endpoints, request/response schemas, and authentication requirements.

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

INFO

Sendings are processed asynchronously by a background service. They are not sent immediately when created.

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:

json
[
  {
    "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:

filtrera
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:

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

Now you can query by campaign:

json
[
  {
    "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)

INFO

The REST API provides endpoints for creating and managing sendings. For querying and analytics, use the Graph API. See Access Control for more on permission patterns.

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

TIP

See the HTTP API Reference for complete endpoint documentation including request/response schemas, error codes, and authentication requirements.

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.

bash
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:

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

Health indicators:

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

Find Recent Failures

json
[
  {
    "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

json
[
  {
    "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.

© 2024 Hantera AB. All rights reserved.