Skip to main content

Provider credentials

Nervly uses a Bring-Your-Own (BYO) credentials model by default. You open accounts directly with your chosen delivery providers, and Nervly orchestrates delivery, failover, rate limiting, and regulatory compliance. A workspace can switch its credential source between BYO, Both and Platform (see Delivery mode).

Delivery mode (BYO, Both, Platform)​

Each workspace has a delivery mode that selects where its provider credentials come from. A workspace inherits the deployment-wide DELIVERY_MODE default (byo at launch) until it sets its own override.

ModeWhat routes your traffic
BYOOnly provider credentials you connect yourself. No platform fallback.
BothYour credentials first, with Nervly's platform accounts as the fallback when you have not connected that provider.
PlatformNervly's provider accounts.

The mode is per-workspace and is set on the console's Providers page or through the control API:

# Read the active mode and the deployment context.
curl https://console.nervly.io/console/providers/delivery-mode \
-H "Cookie: nervly_session=$SESSION_COOKIE"

# Set the mode (owner/admin). Send `null` to clear the override and inherit the
# deployment default again.
curl -X PUT https://console.nervly.io/console/providers/delivery-mode \
-H "Cookie: nervly_session=$SESSION_COOKIE" \
-H "X-CSRF-Token: $CSRF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"delivery_mode":"both"}'

Both and Platform require platform credentials. A deployment that runs DELIVERY_MODE=byo operates no platform provider accounts, so it refuses a both or platform selection with 400 platform_delivery_unavailable rather than silently degrading to BYO. On such a deployment the console shows Both and Platform disabled.

Platform account structure​

Platform mode's customer contract is simple — Nervly's provider accounts send on your behalf and you bring no credentials — but the accounts themselves are structured per workspace:

  • The shared floor is the default. A deployment-wide platform account for each provider is the permanent baseline, and every Platform-mode workspace can send through it immediately.
  • A per-workspace child is an opt-in enhancement. Where the provider supports it, an operator can request a child account for one (workspace, provider) pair. The child has its own credentials, sender resources and provider-enforced separation, and is used only once it is ready.
  • Platform mode never waits on provisioning. A child that is requested or still being provisioned does not block sends — the floor keeps carrying traffic.
  • A broken child fails closed. If a child that had been provisioned is unusable, sends on that provider fail rather than silently falling back to the shared floor.

Isolation is reported honestly with three distinct states — native child · isolated, logical namespace and shared floor — never a single "isolated" badge. See Platform isolation for the full provider list, the provisioning states and the console controls.

BYO delivery receipts are signature-verified against the workspace's own provider webhook signing secret (set per provider on the Providers page). A callback for a BYO workspace with no configured secret is refused, not accepted against the platform secret.

Supported providers​

Nervly natively integrates eleven leading delivery providers across four channels:

ChannelSupported ProvidersCommon Use Case
SMSTermii, Sendchamp, InfobipNigerian carrier delivery, DND routing, OTPs
EmailResend, Postmark, SendGrid, Cloudflare Email, ZeptoMailHigh-reputation transactional & bulk email
PushFirebase Cloud Messaging (FCM), Apple Push Notification service (APNs)Android and iOS device notifications
WhatsAppMeta WhatsApp Cloud APIOfficial WhatsApp Business messaging

Cloudflare Email takes a BYO credential under API Token and Account ID — either {"api_token":"…","account_id":"…"} or account_id:api_token, with a 32-hex account id and a token carrying the Email Sending: Edit permission. It is account-scoped: one token can send from any domain onboarded to Email Sending on that Cloudflare account, so there is no per-tenant isolation. The sending domain must use Cloudflare DNS and be onboarded to Email Sending. Cloudflare Email Sending is public beta and requires the Workers Paid plan.

Envelope encryption architecture​

Provider credentials contain sensitive API secrets, service account JSONs, and Apple private keys (.p8). Nervly guarantees that no plaintext provider secret is ever stored in the database or exposed via any read API.

  1. Per-Record DEK: When a provider is saved, Nervly generates a cryptographically random 32-byte Data Encryption Key (DEK).
  2. Payload Encryption: The credential payload is encrypted using AES-256-GCM with a unique 12-byte random initialization vector (IV) and authenticated tag.
  3. Key Wrapping (KEK): The DEK is wrapped under the platform Key Encryption Key (KEK_V1) using AES-256-GCM.
  4. Database Storage: The database stores only ciphertext, nonce, wrapped_dek, kek_version, and a masked_hint.
  5. Zero Plaintext Reads: Read endpoints (GET /console/providers) return only the masked_hint (e.g., sk_live_...4f2a). Plaintext credentials cannot be queried back.

The envelope above is shared by BYO credentials and platform-provisioned child credentials, with one difference in how the ciphertext is bound. Credentials you connect yourself were sealed as version 0 with no additional authenticated data (aad_version = 0), and existing records are never re-encrypted. Platform child credentials use the AAD-bound variant (aad_version >= 1), which additionally binds the workspace and the child's isolation unit; opening one with the wrong or missing context fails closed rather than returning plaintext.

Connection testing​

Before routing production traffic to a newly added provider, test the connection:

curl -X POST https://console.nervly.io/console/providers/{providerId}/test \
-H "Cookie: nervly_session=$SESSION_COOKIE" \
-H "X-CSRF-Token: $CSRF_TOKEN"

The control plane performs an authentic, zero-delivery API verification call:

  • Termii: Queries account balance endpoint.
  • SendGrid: Queries API key scopes (/v3/scopes).
  • Postmark: Queries server configuration (/server).
  • Cloudflare Email: Reads the account's email-suppression list — an authenticated, non-billable check of the token and account id.
  • Sendchamp: Queries wallet balance.
  • Infobip: Validates account status.
  • FCM / APNs: Generates RS256/ES256 signed JWTs and verifies key format.
  • Meta WhatsApp: Queries app details (/me).

On success, verified_at is updated in the database and logged to the workspace audit log.

Priority and failover order​

Each provider in a workspace has a numeric priority attribute (1 = primary, 2 = secondary fallback, 3 = tertiary fallback).

When dispatching an event:

  1. The Go worker retrieves enabled providers for the channel, sorted by priority ASC.
  2. The worker attempts dispatch against the primary provider.
  3. If the primary fails or its circuit breaker is open, the worker immediately falls back to the next priority provider.
  4. If all providers fail, the message is queued for exponential backoff retry and published to the Dead-Letter Queue (DLQ).

Next​