Custom Providers
Norq ships with ten built-in providers — email (resend, sendgrid), SMS (twilio, bird), push (fcm, apns, expo, webpush), all-channel (suprsend), and the debug console provider. For other delivery services, define custom providers directly in norq.config.yaml — no code required.
Custom providers (HTTP-adapter shape) support email, SMS, and WhatsApp channels. Push is supported only via the four built-in push providers (fcm, apns, expo, webpush) — the HTTP-adapter shape can’t express the per-platform routing, FCM OAuth2 token exchange, APNs JWT signing, or VAPID Web Push encryption that real push delivery requires.
How it works
Custom providers are declarative HTTP adapters. You define the endpoint, auth, and request body in YAML. Norq builds the HTTP request from the compiled template output and the SDK executes it.
norq.config.yaml → compile template → build HTTP request → SDK sends
The provider never sees raw templates — only compiled payloads (HTML, plain text, JSON).
Single-channel example (Mailgun)
providers:
mailgun:
channels: [email]
config:
domain: { required: true }
api_key: { required: true, secret: true }
auth:
type: basic
username: api
password: "{{secrets.api_key}}"
send:
method: POST
url: "https://api.mailgun.net/v3/{{config.domain}}/messages"
body:
from: "notifications@{{config.domain}}"
to: "{{input.recipient}}"
subject: "{{input.subject}}"
html: "{{input.html}}"
response:
success_status: [200]
message_id_path: "$.id"
routing:
email: mailgunMulti-channel example (internal gateway)
A single provider can handle multiple channels with per-channel send definitions:
providers:
internal:
channels: [email, sms]
config:
base_url: { required: true }
api_key: { required: true, secret: true }
auth:
type: bearer
token: "{{secrets.api_key}}"
send:
email:
method: POST
url: "{{config.base_url}}/v1/email"
body:
to: "{{input.recipient}}"
subject: "{{input.subject}}"
html: "{{input.html}}"
response:
success_status: [200]
message_id_path: "$.id"
sms:
method: POST
url: "{{config.base_url}}/v1/sms"
body:
to: "{{input.recipient}}"
text: "{{input.body}}"
response:
success_status: [200]
message_id_path: "$.id"
routing:
email: internal
sms: internalTemplate placeholders
Use Handlebars-style placeholders in URLs, headers, and body values:
| Prefix | Source | Example |
|---|---|---|
{{config.*}} |
Config fields (from YAML or env vars) | {{config.domain}} |
{{secrets.*}} |
Secret config fields (secret: true) |
{{secrets.api_key}} |
{{input.*}} |
Compiled message payload | {{input.subject}}, {{input.html}}, {{input.body}} |
Input fields by channel
| Channel | Available {{input.*}} fields |
|---|---|
recipient, subject, html, text |
|
| sms | recipient, body |
recipient, type, payload |
Authentication
Three auth types are supported:
# Bearer token
auth:
type: bearer
token: "{{secrets.api_key}}"
# HTTP Basic
auth:
type: basic
username: api
password: "{{secrets.api_key}}"
# API key (header or query param)
auth:
type: api_key
header: "X-API-Key" # or: query: "api_key"
value: "{{secrets.api_key}}"Config fields
Config fields define what credentials the provider needs. Mark sensitive values with secret: true — they’re resolved from environment variables and never logged.
config:
api_key: { required: true, secret: true }
domain: { required: true }
from_name: { required: false }Environment variables are substituted at load time using ${VAR} syntax:
providers:
mailgun:
channels: [email]
config:
domain: ${MAILGUN_DOMAIN}
api_key: ${MAILGUN_API_KEY}Secret values can also be loaded from files or inlined directly. See Secret values in the config reference for the three accepted forms (env var ref, file path, inline literal).
Response handling
The response block tells Norq how to interpret the provider’s HTTP response:
response:
success_status: [200, 201, 202]
message_id_path: "$.id"success_status— HTTP status codes that indicate successmessage_id_path— JSONPath to extract the message ID from the response body
Routing
Map channels to providers in the routing: section. The key must match a provider defined under providers::
routing:
email: mailgun
sms: twilio
whatsapp: internalChannels without a routing entry are skipped by norq send (even if the template exists).
Validation
Norq validates custom providers at config load time (error codes P001–P012):
- Missing required config fields
- Unknown channel names
- Invalid auth configuration
- Malformed URL templates
- Missing send definitions for declared channels
Run norq doctor to check your provider configuration.