# Delete an audience Source: https://www.courier.com/docs/api-reference/audiences/delete-an-audience /openapi-specs/openapi.documented.yml delete /audiences/{audience_id} Deletes the specified audience. # Get an audience Source: https://www.courier.com/docs/api-reference/audiences/get-an-audience /openapi-specs/openapi.documented.yml get /audiences/{audience_id} Returns the specified audience by id. # List all audiences Source: https://www.courier.com/docs/api-reference/audiences/list-all-audiences /openapi-specs/openapi.documented.yml get /audiences Get the audiences associated with the authorization token. # List audience members Source: https://www.courier.com/docs/api-reference/audiences/list-audience-members /openapi-specs/openapi.documented.yml get /audiences/{audience_id}/members Get list of members of an audience. # Update an audience Source: https://www.courier.com/docs/api-reference/audiences/update-an-audience /openapi-specs/openapi.documented.yml put /audiences/{audience_id} Creates or updates audience. # Get all audit events Source: https://www.courier.com/docs/api-reference/audit-events/get-all-audit-events /openapi-specs/openapi.documented.yml get /audit-events Fetch the list of audit events # Get an audit event Source: https://www.courier.com/docs/api-reference/audit-events/get-an-audit-event /openapi-specs/openapi.documented.yml get /audit-events/{audit-event-id} Fetch a specific audit event by ID. # Create a JWT Source: https://www.courier.com/docs/api-reference/authentication/create-a-jwt /openapi-specs/openapi.documented.yml post /auth/issue-token Returns a new access token. # Invoke an Ad Hoc Automation Source: https://www.courier.com/docs/api-reference/automations/invoke-an-ad-hoc-automation /openapi-specs/openapi.documented.yml post /automations/invoke Invoke an ad hoc automation run. This endpoint accepts a JSON payload with a series of automation steps. For information about what steps are available, checkout the ad hoc automation guide [here](https://www.courier.com/docs/automations/steps/). # Invoke an Automation Source: https://www.courier.com/docs/api-reference/automations/invoke-an-automation /openapi-specs/openapi.documented.yml post /automations/{templateId}/invoke Invoke an automation run from an automation template. # List Automations Source: https://www.courier.com/docs/api-reference/automations/list-automations /openapi-specs/openapi.documented.yml get /automations Get the list of automations. # Create a new brand Source: https://www.courier.com/docs/api-reference/brands/create-a-new-brand /openapi-specs/openapi.documented.yml post /brands Create a new brand. Requires `name` and `settings` (with at least `colors.primary` and `colors.secondary`). # Delete a brand Source: https://www.courier.com/docs/api-reference/brands/delete-a-brand /openapi-specs/openapi.documented.yml delete /brands/{brand_id} Delete a brand by brand ID. # Get a brand Source: https://www.courier.com/docs/api-reference/brands/get-a-brand /openapi-specs/openapi.documented.yml get /brands/{brand_id} Fetch a specific brand by brand ID. # List brands Source: https://www.courier.com/docs/api-reference/brands/list-brands /openapi-specs/openapi.documented.yml get /brands Get the list of brands. # Replace a brand Source: https://www.courier.com/docs/api-reference/brands/replace-a-brand /openapi-specs/openapi.documented.yml put /brands/{brand_id} Replace an existing brand with the supplied values. # Add users Source: https://www.courier.com/docs/api-reference/bulk/add-users /openapi-specs/openapi.documented.yml post /bulk/{job_id} Ingest user data into a Bulk Job. **Important**: For email-based bulk jobs, each user must include `profile.email` for provider routing to work correctly. The `to.email` field is not sufficient for email provider routing. # Create a bulk job Source: https://www.courier.com/docs/api-reference/bulk/create-a-bulk-job /openapi-specs/openapi.documented.yml post /bulk Creates a new bulk job for sending messages to multiple recipients. **Required**: `message.event` (event ID or notification ID) **Optional (V2 format)**: `message.template` (notification ID) or `message.content` (Elemental content) can be provided to override the notification associated with the event. # Get a Job Source: https://www.courier.com/docs/api-reference/bulk/get-a-job /openapi-specs/openapi.documented.yml get /bulk/{job_id} Get a bulk job # Get users Source: https://www.courier.com/docs/api-reference/bulk/get-users /openapi-specs/openapi.documented.yml get /bulk/{job_id}/users Get Bulk Job Users # Run a job Source: https://www.courier.com/docs/api-reference/bulk/run-a-job /openapi-specs/openapi.documented.yml post /bulk/{job_id}/run Run a bulk job # Create or Update a Tenant Template Source: https://www.courier.com/docs/api-reference/courier-create/create-or-update-a-tenant-template /openapi-specs/openapi.documented.yml put /tenants/{tenant_id}/templates/{template_id} Creates or updates a notification template for a tenant. If the template already exists for the tenant, it will be updated (200). Otherwise, a new template is created (201). Optionally publishes the template immediately if the `published` flag is set to true. # Delete a Tenant Template Source: https://www.courier.com/docs/api-reference/courier-create/delete-a-tenant-template /openapi-specs/openapi.documented.yml delete /tenants/{tenant_id}/templates/{template_id} Deletes the tenant's notification template with the given `template_id`. Returns **204 No Content** with an empty body on success. Returns **404** if there is no template with this ID for the tenant, including a second `DELETE` after a successful removal. # Get a Specific Template Version Source: https://www.courier.com/docs/api-reference/courier-create/get-a-specific-template-version /openapi-specs/openapi.documented.yml get /tenants/{tenant_id}/templates/{template_id}/versions/{version} Fetches a specific version of a tenant template. Supports the following version formats: - `latest` - The most recent version of the template - `published` - The currently published version - `v{version}` - A specific version (e.g., "v1", "v2", "v1.0.0") # Get a Template in Tenant Source: https://www.courier.com/docs/api-reference/courier-create/get-a-template-in-tenant /openapi-specs/openapi.documented.yml get /tenants/{tenant_id}/templates/{template_id} # List Templates in Tenant Source: https://www.courier.com/docs/api-reference/courier-create/list-templates-in-tenant /openapi-specs/openapi.documented.yml get /tenants/{tenant_id}/templates # Publish a Tenant Template Source: https://www.courier.com/docs/api-reference/courier-create/publish-a-tenant-template /openapi-specs/openapi.documented.yml post /tenants/{tenant_id}/templates/{template_id}/publish Publishes a specific version of a notification template for a tenant. The template must already exist in the tenant's notification map. If no version is specified, defaults to publishing the "latest" version. # Add multiple tokens to user Source: https://www.courier.com/docs/api-reference/device-tokens/add-multiple-tokens-to-user /openapi-specs/openapi.documented.yml put /users/{user_id}/tokens Adds multiple tokens to a user and overwrites matching existing tokens. # Add single token to user Source: https://www.courier.com/docs/api-reference/device-tokens/add-single-token-to-user /openapi-specs/openapi.documented.yml put /users/{user_id}/tokens/{token} Adds a single token to a user and overwrites a matching existing token. # Delete User Token Source: https://www.courier.com/docs/api-reference/device-tokens/delete-user-token /openapi-specs/openapi.documented.yml delete /users/{user_id}/tokens/{token} # Get all tokens Source: https://www.courier.com/docs/api-reference/device-tokens/get-all-tokens /openapi-specs/openapi.documented.yml get /users/{user_id}/tokens Gets all tokens available for a :user_id # Get single token Source: https://www.courier.com/docs/api-reference/device-tokens/get-single-token /openapi-specs/openapi.documented.yml get /users/{user_id}/tokens/{token} Get single token available for a `:token` # Update a token Source: https://www.courier.com/docs/api-reference/device-tokens/update-a-token /openapi-specs/openapi.documented.yml patch /users/{user_id}/tokens/{token} Apply a JSON Patch (RFC 6902) to the specified token. # List digest instances Source: https://www.courier.com/docs/api-reference/digests/list-digest-instances /openapi-specs/openapi.documented.yml get /digests/schedules/{schedule_id}/instances List the digest instances for a schedule. Each instance represents the events accumulated for a single user against the schedule, and can be used to monitor digest accumulation before the digest is released. # Release a digest early Source: https://www.courier.com/docs/api-reference/digests/release-a-digest-early /openapi-specs/openapi.documented.yml post /digests/schedules/{schedule_id}/trigger Send a digest now instead of waiting for its scheduled time, so your users get what they have collected so far right away. # Courier Track Event Source: https://www.courier.com/docs/api-reference/inbound/courier-track-event /openapi-specs/openapi.documented.yml post /inbound/courier # Archive a journey Source: https://www.courier.com/docs/api-reference/journeys/archive-a-journey /openapi-specs/openapi.documented.yml delete /journeys/{templateId} Archive a journey. Archived journeys cannot be invoked. Existing journey runs continue to completion. # Archive a journey-scoped notification template Source: https://www.courier.com/docs/api-reference/journeys/archive-a-journey-scoped-notification-template /openapi-specs/openapi.documented.yml delete /journeys/{templateId}/templates/{notificationId} Archive the journey-scoped notification template. Archived templates cannot be sent. # Cancel journey runs Source: https://www.courier.com/docs/api-reference/journeys/cancel-journey-runs /openapi-specs/openapi.documented.yml post /journeys/cancel Cancel journey runs. The request body must include EXACTLY ONE of `cancelation_token` (cancels every run associated with the token) or `run_id` (cancels a single tenant-scoped run). Supplying both or neither returns a `400`. A `run_id` that does not match a run for the tenant returns `404`. Cancelation is idempotent: a run that has already finished (`PROCESSED`/`ERROR`) or was already `CANCELED` is left unchanged and its current status is returned. # Create a journey Source: https://www.courier.com/docs/api-reference/journeys/create-a-journey /openapi-specs/openapi.documented.yml post /journeys Create a journey. Defaults to `DRAFT` state; pass `state: "PUBLISHED"` to publish on create. Send nodes are not allowed on `POST`. The standard flow is: create the journey shell here, add notification templates with `POST /journeys/{templateId}/templates`, then wire them into the journey with `PUT /journeys/{templateId}`. Call `POST /journeys/{templateId}/publish` to publish a draft after the fact. # Create a notification template scoped to a journey Source: https://www.courier.com/docs/api-reference/journeys/create-a-notification-template-scoped-to-a-journey /openapi-specs/openapi.documented.yml post /journeys/{templateId}/templates Create a notification template scoped to this journey. Defaults to `DRAFT` state; pass `state: "PUBLISHED"` to publish on create. # Fetch a journey by id Source: https://www.courier.com/docs/api-reference/journeys/fetch-a-journey-by-id /openapi-specs/openapi.documented.yml get /journeys/{templateId} Fetch a journey by id. Pass `?version=draft` (default `published`) to retrieve the working draft, or `?version=vN` to retrieve a historical version. # Fetch a journey-scoped notification template by id Source: https://www.courier.com/docs/api-reference/journeys/fetch-a-journey-scoped-notification-template-by-id /openapi-specs/openapi.documented.yml get /journeys/{templateId}/templates/{notificationId} Fetch a journey-scoped notification template by id. Pass `?version=draft` (default `published`) to retrieve the working draft, or `?version=vN` for a historical version. # Fetch the content of a journey-scoped notification template Source: https://www.courier.com/docs/api-reference/journeys/fetch-the-content-of-a-journey-scoped-notification-template /openapi-specs/openapi.documented.yml get /journeys/{templateId}/templates/{notificationId}/content Retrieve the elemental content of a journey-scoped notification template. The response contains the versioned elements along with their content checksums, which can be used to detect changes between versions. Pass `?version=draft` (default `published`) to retrieve the working draft, or `?version=vN` for a historical version. # Invoke a journey Source: https://www.courier.com/docs/api-reference/journeys/invoke-a-journey /openapi-specs/openapi.documented.yml post /journeys/{templateId}/invoke Invoke a journey by id or alias to start a new run. The response includes a `runId` identifying the run. # List Journeys Source: https://www.courier.com/docs/api-reference/journeys/list-journeys /openapi-specs/openapi.documented.yml get /journeys Get the list of journeys. # List notification templates scoped to a journey Source: https://www.courier.com/docs/api-reference/journeys/list-notification-templates-scoped-to-a-journey /openapi-specs/openapi.documented.yml get /journeys/{templateId}/templates List notification templates scoped to this journey. Journey-scoped notification templates can only be referenced from `send` nodes within the same journey. # List versions of a journey Source: https://www.courier.com/docs/api-reference/journeys/list-versions-of-a-journey /openapi-specs/openapi.documented.yml get /journeys/{templateId}/versions List published versions of a journey, ordered most recent first. # List versions of a journey-scoped notification template Source: https://www.courier.com/docs/api-reference/journeys/list-versions-of-a-journey-scoped-notification-template /openapi-specs/openapi.documented.yml get /journeys/{templateId}/templates/{notificationId}/versions List published versions of the journey-scoped notification template, ordered most recent first. # Publish a journey Source: https://www.courier.com/docs/api-reference/journeys/publish-a-journey /openapi-specs/openapi.documented.yml post /journeys/{templateId}/publish Publish the current draft as a new version. Body is optional; pass `{ "version": "vN" }` to roll back to a prior version instead. Returns 404 if the journey has no draft to publish. # Publish a journey-scoped notification template Source: https://www.courier.com/docs/api-reference/journeys/publish-a-journey-scoped-notification-template /openapi-specs/openapi.documented.yml post /journeys/{templateId}/templates/{notificationId}/publish Publish the current draft of the journey-scoped notification template as a new version. Optionally roll back to a prior version by passing `{ "version": "vN" }`. # Replace a journey Source: https://www.courier.com/docs/api-reference/journeys/replace-a-journey /openapi-specs/openapi.documented.yml put /journeys/{templateId} Replace the journey draft. Updates the working draft only; call `POST /journeys/{templateId}/publish` to make it live, or pass `state: "PUBLISHED"` in this request to publish immediately. Send-node `template` ids must already exist and be scoped to this journey, and node ids must not be claimed by another journey. # Replace a journey-scoped notification template Source: https://www.courier.com/docs/api-reference/journeys/replace-a-journey-scoped-notification-template /openapi-specs/openapi.documented.yml put /journeys/{templateId}/templates/{notificationId} Replace the journey-scoped notification template draft. # Replace a single locale of a journey-scoped notification template Source: https://www.courier.com/docs/api-reference/journeys/replace-a-single-locale-of-a-journey-scoped-notification-template /openapi-specs/openapi.documented.yml put /journeys/{templateId}/templates/{notificationId}/locales/{localeId} Set locale-specific content overrides for a journey-scoped notification template. Each element override must reference an existing element by ID. # Replace the content of a journey-scoped notification template Source: https://www.courier.com/docs/api-reference/journeys/replace-the-content-of-a-journey-scoped-notification-template /openapi-specs/openapi.documented.yml put /journeys/{templateId}/templates/{notificationId}/content Replace the elemental content of a journey-scoped notification template. Overwrites all elements in the template draft with the provided content. # Add subscribers to a list Source: https://www.courier.com/docs/api-reference/lists/add-subscribers-to-a-list /openapi-specs/openapi.documented.yml post /lists/{list_id}/subscriptions Subscribes additional users to the list, without modifying existing subscriptions. If the list does not exist, it will be automatically created. # Delete a list Source: https://www.courier.com/docs/api-reference/lists/delete-a-list /openapi-specs/openapi.documented.yml delete /lists/{list_id} Delete a list by list ID. # Get a list Source: https://www.courier.com/docs/api-reference/lists/get-a-list /openapi-specs/openapi.documented.yml get /lists/{list_id} Returns a list based on the list ID provided. # Get all lists Source: https://www.courier.com/docs/api-reference/lists/get-all-lists /openapi-specs/openapi.documented.yml get /lists Returns all of the lists, with the ability to filter based on a pattern. # Get the subscriptions for a list Source: https://www.courier.com/docs/api-reference/lists/get-the-subscriptions-for-a-list /openapi-specs/openapi.documented.yml get /lists/{list_id}/subscriptions Get the list's subscriptions. # Restore a list Source: https://www.courier.com/docs/api-reference/lists/restore-a-list /openapi-specs/openapi.documented.yml put /lists/{list_id}/restore Restore a previously deleted list. # Subscribe a single user profile to a list Source: https://www.courier.com/docs/api-reference/lists/subscribe-a-single-user-profile-to-a-list /openapi-specs/openapi.documented.yml put /lists/{list_id}/subscriptions/{user_id} Subscribe a user to an existing list (note: if the List does not exist, it will be automatically created). # Subscribe users to a list Source: https://www.courier.com/docs/api-reference/lists/subscribe-users-to-a-list /openapi-specs/openapi.documented.yml put /lists/{list_id}/subscriptions Subscribes the users to the list, overwriting existing subscriptions. If the list does not exist, it will be automatically created. # Unsubscribe a user profile from a list Source: https://www.courier.com/docs/api-reference/lists/unsubscribe-a-user-profile-from-a-list /openapi-specs/openapi.documented.yml delete /lists/{list_id}/subscriptions/{user_id} Delete a subscription to a list by list ID and user ID. # Update a list Source: https://www.courier.com/docs/api-reference/lists/update-a-list /openapi-specs/openapi.documented.yml put /lists/{list_id} Create or replace an existing list with the supplied values. # Archive Notification Template Source: https://www.courier.com/docs/api-reference/notification-templates/archive-notification-template /openapi-specs/openapi.documented.yml delete /notifications/{id} Archive a notification template. # Cancel Submission Source: https://www.courier.com/docs/api-reference/notification-templates/cancel-submission /openapi-specs/openapi.documented.yml delete /notifications/{id}/{submissionId}/checks Cancel a submission for a notification template. # Create Notification Template Source: https://www.courier.com/docs/api-reference/notification-templates/create-notification-template /openapi-specs/openapi.documented.yml post /notifications Create a notification template. Requires all fields in the notification object. Templates are created in draft state by default. # Get Notification Content Source: https://www.courier.com/docs/api-reference/notification-templates/get-notification-content /openapi-specs/openapi.documented.yml get /notifications/{id}/content Retrieve the content of a notification template. The response shape depends on whether the template uses V1 (blocks/channels) or V2 (elemental) content. Use the `version` query parameter to select draft, published, or a specific historical version. # Get Notification Template Source: https://www.courier.com/docs/api-reference/notification-templates/get-notification-template /openapi-specs/openapi.documented.yml get /notifications/{id} Retrieve a notification template by ID. Returns the published version by default. Pass version=draft to retrieve an unpublished template. # Get Submission Checks Source: https://www.courier.com/docs/api-reference/notification-templates/get-submission-checks /openapi-specs/openapi.documented.yml get /notifications/{id}/{submissionId}/checks Retrieve the submission checks for a notification template. # List Notification Template Versions Source: https://www.courier.com/docs/api-reference/notification-templates/list-notification-template-versions /openapi-specs/openapi.documented.yml get /notifications/{id}/versions List versions of a notification template. # List Notification Templates Source: https://www.courier.com/docs/api-reference/notification-templates/list-notification-templates /openapi-specs/openapi.documented.yml get /notifications List notification templates in your workspace. # Publish Notification Template Source: https://www.courier.com/docs/api-reference/notification-templates/publish-notification-template /openapi-specs/openapi.documented.yml post /notifications/{id}/publish Publish a notification template. Publishes the current draft by default. Pass a version in the request body to publish a specific historical version. # Put Notification Content Source: https://www.courier.com/docs/api-reference/notification-templates/put-notification-content /openapi-specs/openapi.documented.yml put /notifications/{id}/content Replace the elemental content of a notification template. Overwrites all elements in the template with the provided content. Only supported for V2 (elemental) templates. # Put Notification Element Source: https://www.courier.com/docs/api-reference/notification-templates/put-notification-element /openapi-specs/openapi.documented.yml put /notifications/{id}/elements/{elementId} Update a single element within a notification template. Only supported for V2 (elemental) templates. # Put Notification Locale Source: https://www.courier.com/docs/api-reference/notification-templates/put-notification-locale /openapi-specs/openapi.documented.yml put /notifications/{id}/locales/{localeId} Set locale-specific content overrides for a notification template. Each element override must reference an existing element by ID. Only supported for V2 (elemental) templates. # Replace Notification Template Source: https://www.courier.com/docs/api-reference/notification-templates/replace-notification-template /openapi-specs/openapi.documented.yml put /notifications/{id} Replace a notification template. All fields are required. # Replace Submission Checks Source: https://www.courier.com/docs/api-reference/notification-templates/replace-submission-checks /openapi-specs/openapi.documented.yml put /notifications/{id}/{submissionId}/checks Replace the submission checks for a notification template. # Create a provider Source: https://www.courier.com/docs/api-reference/providers/create-a-provider /openapi-specs/openapi.documented.yml post /providers Create a new provider configuration. The `provider` field must be a known Courier provider key (see catalog). # Delete a provider Source: https://www.courier.com/docs/api-reference/providers/delete-a-provider /openapi-specs/openapi.documented.yml delete /providers/{id} Delete a provider configuration. Returns 409 if the provider is still referenced by routing or notifications. # Get a provider Source: https://www.courier.com/docs/api-reference/providers/get-a-provider /openapi-specs/openapi.documented.yml get /providers/{id} Fetch a single provider configuration by ID. # List available provider types Source: https://www.courier.com/docs/api-reference/providers/list-available-provider-types /openapi-specs/openapi.documented.yml get /providers/catalog Returns the catalog of available provider types with their display names, descriptions, and configuration schema fields (snake_case, with `type` and `required`). Providers with no configurable schema return only `provider`, `name`, and `description`. # List providers Source: https://www.courier.com/docs/api-reference/providers/list-providers /openapi-specs/openapi.documented.yml get /providers List configured provider integrations for the current workspace. Supports cursor-based pagination. # Update a provider Source: https://www.courier.com/docs/api-reference/providers/update-a-provider /openapi-specs/openapi.documented.yml put /providers/{id} Replace an existing provider configuration. The `provider` key is required and determines which provider-specific settings schema is applied. All other fields are optional — omitted fields are cleared from the stored configuration (this is a full replacement, not a partial merge). Changing the provider type for an existing configuration is not supported. # Archive Routing Strategy Source: https://www.courier.com/docs/api-reference/routing-strategies/archive-routing-strategy /openapi-specs/openapi.documented.yml delete /routing-strategies/{id} Archive a routing strategy. The strategy must not have associated notification templates. Unlink all templates before archiving. # Create Routing Strategy Source: https://www.courier.com/docs/api-reference/routing-strategies/create-routing-strategy /openapi-specs/openapi.documented.yml post /routing-strategies Create a routing strategy. Requires a name and routing configuration at minimum. Channels and providers default to empty if omitted. # Get Routing Strategy Source: https://www.courier.com/docs/api-reference/routing-strategies/get-routing-strategy /openapi-specs/openapi.documented.yml get /routing-strategies/{id} Retrieve a routing strategy by ID. Returns the full entity including routing content and metadata. # List Associated Notifications Source: https://www.courier.com/docs/api-reference/routing-strategies/list-associated-notifications /openapi-specs/openapi.documented.yml get /routing-strategies/{id}/notifications List notification templates associated with a routing strategy. Includes template metadata only, not full content. # List Routing Strategies Source: https://www.courier.com/docs/api-reference/routing-strategies/list-routing-strategies /openapi-specs/openapi.documented.yml get /routing-strategies List routing strategies in your workspace. Returns metadata only (no routing/channels/providers content). Use GET /routing-strategies/{id} for full details. # Replace Routing Strategy Source: https://www.courier.com/docs/api-reference/routing-strategies/replace-routing-strategy /openapi-specs/openapi.documented.yml put /routing-strategies/{id} Replace a routing strategy. Full document replacement; the caller must send the complete desired state. Missing optional fields are cleared. # Send a message Source: https://www.courier.com/docs/api-reference/send/send-a-message /openapi-specs/openapi.documented.yml post /send Send a message to one or more recipients. # Archive message Source: https://www.courier.com/docs/api-reference/sent-messages/archive-message /openapi-specs/openapi.documented.yml put /requests/{request_id}/archive # Cancel message Source: https://www.courier.com/docs/api-reference/sent-messages/cancel-message /openapi-specs/openapi.documented.yml post /messages/{message_id}/cancel Cancel a message that is currently in the process of being delivered. A well-formatted API call to the cancel message API will return either `200` status code for a successful cancellation or `409` status code for an unsuccessful cancellation. Both cases will include the actual message record in the response body (see details below). # Get message Source: https://www.courier.com/docs/api-reference/sent-messages/get-message /openapi-specs/openapi.documented.yml get /messages/{message_id} Fetch the status of a message you've previously sent. # Get message content Source: https://www.courier.com/docs/api-reference/sent-messages/get-message-content /openapi-specs/openapi.documented.yml get /messages/{message_id}/output # Get message history Source: https://www.courier.com/docs/api-reference/sent-messages/get-message-history /openapi-specs/openapi.documented.yml get /messages/{message_id}/history Fetch the array of events of a message you've previously sent. # List messages Source: https://www.courier.com/docs/api-reference/sent-messages/list-messages /openapi-specs/openapi.documented.yml get /messages Fetch the statuses of messages you've previously sent. # Resend message Source: https://www.courier.com/docs/api-reference/sent-messages/resend-message /openapi-specs/openapi.documented.yml post /messages/{message_id}/resend Resend a previously sent message. The original send request is loaded from storage and a brand-new send is enqueued for the same recipient and content, producing a **new** `messageId` — the original message is not modified. Throttled by a per-message rate limit; a repeat inside the limit window returns `429 Too Many Requests`. # Create or Replace a Tenant Source: https://www.courier.com/docs/api-reference/tenants/create-or-replace-a-tenant /openapi-specs/openapi.documented.yml put /tenants/{tenant_id} # Create or Replace Default Preferences For Topic Source: https://www.courier.com/docs/api-reference/tenants/create-or-replace-default-preferences-for-topic /openapi-specs/openapi.documented.yml put /tenants/{tenant_id}/default_preferences/items/{topic_id} # Delete a Tenant Source: https://www.courier.com/docs/api-reference/tenants/delete-a-tenant /openapi-specs/openapi.documented.yml delete /tenants/{tenant_id} # Get a List of Tenants Source: https://www.courier.com/docs/api-reference/tenants/get-a-list-of-tenants /openapi-specs/openapi.documented.yml get /tenants # Get a Tenant Source: https://www.courier.com/docs/api-reference/tenants/get-a-tenant /openapi-specs/openapi.documented.yml get /tenants/{tenant_id} # Get Users in Tenant Source: https://www.courier.com/docs/api-reference/tenants/get-users-in-tenant /openapi-specs/openapi.documented.yml get /tenants/{tenant_id}/users # Remove Default Preferences For Topic Source: https://www.courier.com/docs/api-reference/tenants/remove-default-preferences-for-topic /openapi-specs/openapi.documented.yml delete /tenants/{tenant_id}/default_preferences/items/{topic_id} # Get a translation Source: https://www.courier.com/docs/api-reference/translations/get-a-translation /openapi-specs/openapi.documented.yml get /translations/{domain}/{locale} Get translations by locale # Update translations by locale Source: https://www.courier.com/docs/api-reference/translations/update-translations-by-locale /openapi-specs/openapi.documented.yml put /translations/{domain}/{locale} Update a translation # Delete user subscription topic Source: https://www.courier.com/docs/api-reference/user-preferences/delete-user-subscription-topic /openapi-specs/openapi.documented.yml delete /users/{user_id}/preferences/{topic_id} Remove a user's preferences for a specific subscription topic, resetting the topic to its effective default. This operation is idempotent: deleting a preference that does not exist succeeds with no error. # Get user subscription topic Source: https://www.courier.com/docs/api-reference/user-preferences/get-user-subscription-topic /openapi-specs/openapi.documented.yml get /users/{user_id}/preferences/{topic_id} Fetch user preferences for a specific subscription topic. # Get user's preferences Source: https://www.courier.com/docs/api-reference/user-preferences/get-users-preferences /openapi-specs/openapi.documented.yml get /users/{user_id}/preferences Fetch all user preferences. # Replace user preferences in bulk Source: https://www.courier.com/docs/api-reference/user-preferences/replace-user-preferences-in-bulk /openapi-specs/openapi.documented.yml put /users/{user_id}/preferences Replace a user's complete set of preference overrides in a single request. The topics in the request body become the recipient's entire set of overrides: listed topics are created or updated, and every existing override that is not included in the body is reset to its topic default. Submitting an empty `topics` array is a valid clear-all that resets every existing override. This operation is validation-atomic (all-or-nothing): structural validation fails fast with a single `400`, and if any topic is semantically invalid (an unknown topic, a `REQUIRED` topic that cannot be opted out, or a custom routing request that is not available on the workspace's plan) the request returns a single `400` aggregating every failure in `errors` and writes nothing. On success it returns `200` with `items` (the complete resulting override set) and `deleted` (the ids of the overrides that were reset to default). Every `topic_id` in the response — in `items`, `deleted`, and any `errors` — is returned in Courier's canonical topic id form, regardless of the form supplied in the request. # Update or Create user preferences for a specific subscription topic Source: https://www.courier.com/docs/api-reference/user-preferences/update-or-create-user-preferences-for-a-specific-subscription-topic /openapi-specs/openapi.documented.yml put /users/{user_id}/preferences/{topic_id} Update or Create user preferences for a specific subscription topic. # Update user preferences in bulk Source: https://www.courier.com/docs/api-reference/user-preferences/update-user-preferences-in-bulk /openapi-specs/openapi.documented.yml post /users/{user_id}/preferences Additively create or update a user's preferences for one or more subscription topics in a single request. Only the topics included in the request body are created or updated; any existing overrides for topics not listed are left untouched. Structural validation of the request body fails fast with a single `400`. Beyond that, each topic is processed independently (partial-success, not all-or-nothing): valid topics are written and returned in `items`, while topics that cannot be applied are collected in `errors` with a per-topic `reason` (for example an unknown topic, a `REQUIRED` topic that cannot be opted out, a custom routing request that is not available on the workspace's plan, or a write failure). The request therefore returns `200` with both lists whenever the body is structurally valid. Every `topic_id` in the response — in both `items` and `errors` — is returned in Courier's canonical topic id form, regardless of the form supplied in the request. # Create a profile Source: https://www.courier.com/docs/api-reference/user-profiles/create-a-profile /openapi-specs/openapi.documented.yml post /profiles/{user_id} Merge the supplied values with an existing profile or create a new profile if one doesn't already exist. # Delete a profile Source: https://www.courier.com/docs/api-reference/user-profiles/delete-a-profile /openapi-specs/openapi.documented.yml delete /profiles/{user_id} Deletes the specified user profile. # Delete list subscriptions Source: https://www.courier.com/docs/api-reference/user-profiles/delete-list-subscriptions /openapi-specs/openapi.documented.yml delete /profiles/{user_id}/lists Removes all list subscriptions for given user. # Get a profile Source: https://www.courier.com/docs/api-reference/user-profiles/get-a-profile /openapi-specs/openapi.documented.yml get /profiles/{user_id} Returns the specified user profile. # Get list subscriptions Source: https://www.courier.com/docs/api-reference/user-profiles/get-list-subscriptions /openapi-specs/openapi.documented.yml get /profiles/{user_id}/lists Returns the subscribed lists for a specified user. # Replace a profile Source: https://www.courier.com/docs/api-reference/user-profiles/replace-a-profile /openapi-specs/openapi.documented.yml put /profiles/{user_id} When using `PUT`, be sure to include all the key-value pairs required by the recipient's profile. Any key-value pairs that exist in the profile but fail to be included in the `PUT` request will be removed from the profile. Remember, a `PUT` update is a full replacement of the data. For partial updates, use the [Patch](https://www.courier.com/docs/reference/profiles/patch/) request. # Subscribe to one or more lists Source: https://www.courier.com/docs/api-reference/user-profiles/subscribe-to-one-or-more-lists /openapi-specs/openapi.documented.yml post /profiles/{user_id}/lists Subscribes the given user to one or more lists. If the list does not exist, it will be created. # Update a profile Source: https://www.courier.com/docs/api-reference/user-profiles/update-a-profile /openapi-specs/openapi.documented.yml patch /profiles/{user_id} # Add a User to a Single Tenant Source: https://www.courier.com/docs/api-reference/user-tenants/add-a-user-to-a-single-tenant /openapi-specs/openapi.documented.yml put /users/{user_id}/tenants/{tenant_id} This endpoint is used to add a single tenant. A custom profile can also be supplied with the tenant. This profile will be merged with the user's main profile when sending to the user with that tenant. # Add a User to Multiple Tenants Source: https://www.courier.com/docs/api-reference/user-tenants/add-a-user-to-multiple-tenants /openapi-specs/openapi.documented.yml put /users/{user_id}/tenants This endpoint is used to add a user to multiple tenants in one call. A custom profile can also be supplied for each tenant. This profile will be merged with the user's main profile when sending to the user with that tenant. # Get tenants associated with a given user Source: https://www.courier.com/docs/api-reference/user-tenants/get-tenants-associated-with-a-given-user /openapi-specs/openapi.documented.yml get /users/{user_id}/tenants Returns a paginated list of user tenant associations. # Remove User from a Tenant Source: https://www.courier.com/docs/api-reference/user-tenants/remove-user-from-a-tenant /openapi-specs/openapi.documented.yml delete /users/{user_id}/tenants/{tenant_id} Removes a user from the supplied tenant. # Remove User From All Associated Tenants Source: https://www.courier.com/docs/api-reference/user-tenants/remove-user-from-all-associated-tenants /openapi-specs/openapi.documented.yml delete /users/{user_id}/tenants Removes a user from any tenants they may have been associated with. # Archive Topic in Section Source: https://www.courier.com/docs/api-reference/workspace-preferences/archive-topic-in-section /openapi-specs/openapi.documented.yml delete /preferences/sections/{section_id}/topics/{topic_id} Archive a topic and remove it from its workspace preference. Same 404 rules as GET. # Archive Workspace Preference Source: https://www.courier.com/docs/api-reference/workspace-preferences/archive-workspace-preference /openapi-specs/openapi.documented.yml delete /preferences/sections/{section_id} Archive a workspace preference. The workspace preference must be empty: delete its topics first, otherwise the request fails with 409. # Create Topic in Section Source: https://www.courier.com/docs/api-reference/workspace-preferences/create-topic-in-section /openapi-specs/openapi.documented.yml post /preferences/sections/{section_id}/topics Create a subscription preference topic inside a workspace preference. Fails with 404 if the workspace preference does not exist. The topic id is generated and returned. # Create Workspace Preference Source: https://www.courier.com/docs/api-reference/workspace-preferences/create-workspace-preference /openapi-specs/openapi.documented.yml post /preferences/sections Create a workspace preference. The workspace preference id is generated and returned. Topics are created inside a workspace preference via POST /preferences/sections/{section_id}/topics. # Get Topic in Section Source: https://www.courier.com/docs/api-reference/workspace-preferences/get-topic-in-section /openapi-specs/openapi.documented.yml get /preferences/sections/{section_id}/topics/{topic_id} Retrieve a topic within a workspace preference. Returns 404 if the workspace preference does not exist, the topic does not exist, or the topic belongs to a different workspace preference. # Get Workspace Preference Source: https://www.courier.com/docs/api-reference/workspace-preferences/get-workspace-preference /openapi-specs/openapi.documented.yml get /preferences/sections/{section_id} Retrieve a workspace preference by id, including its topics. # List Topics in Section Source: https://www.courier.com/docs/api-reference/workspace-preferences/list-topics-in-section /openapi-specs/openapi.documented.yml get /preferences/sections/{section_id}/topics List the topics in a workspace preference. # List Workspace Preferences Source: https://www.courier.com/docs/api-reference/workspace-preferences/list-workspace-preferences /openapi-specs/openapi.documented.yml get /preferences/sections List the workspace's preferences. Each workspace preference embeds its topics. Scoped to the workspace of the API key. # Publish Preferences Page Source: https://www.courier.com/docs/api-reference/workspace-preferences/publish-preferences-page /openapi-specs/openapi.documented.yml post /preferences/publish Publish the workspace's preferences page. Takes a snapshot of every workspace preference with its topics under a new published version, making the current state visible on the hosted preferences page (non-draft). # Replace Topic in Section Source: https://www.courier.com/docs/api-reference/workspace-preferences/replace-topic-in-section /openapi-specs/openapi.documented.yml put /preferences/sections/{section_id}/topics/{topic_id} Replace a topic within a workspace preference. Full document replacement; missing optional fields are cleared. Same 404 rules as GET. # Replace Workspace Preference Source: https://www.courier.com/docs/api-reference/workspace-preferences/replace-workspace-preference /openapi-specs/openapi.documented.yml put /preferences/sections/{section_id} Replace a workspace preference. Full document replacement; missing optional fields are cleared. Topics attached to the workspace preference are unaffected. # Customer Data Platforms (CDP) Source: https://www.courier.com/docs/external-integrations/cdp/intro-to-cdp Integrate Customer Data Platforms with Courier to sync user data and trigger notifications from CDP events. Customer Data Platforms (CDPs) collect, unify, and activate customer data across your tech stack. Courier integrates with popular CDPs to automatically sync user profiles and trigger notifications based on customer behavior and events. When you integrate a CDP with Courier, you can: * **Sync profiles automatically** - User profiles are created and updated in Courier in real-time from your CDP data * **Trigger notifications from events** - Customer events like purchases, sign-ups, or usage milestones automatically trigger Courier workflows * **Personalize across channels** - Use CDP data to determine the best channel and personalize content for each user ## Available Integrations Send events to Courier and track notification events in Segment Sync events with Courier and send notification data to RudderStack # RudderStack CDP Integration Source: https://www.courier.com/docs/external-integrations/cdp/rudderstack Connect Courier and RudderStack bidirectionally: route identify, track, and group events into automations and stream Courier notification events back. Courier's RudderStack integration lets you send event data from a variety of event sources to Courier. You can also configure RudderStack as a source inside the Courier workspace by adding your write key and data plane URL, allowing Courier events to be sent to RudderStack. ## RudderStack to Courier RudderStack supports Courier as a destination. Once you configure the Courier destination inside RudderStack, you can connect one or more sources and events will start flowing into Courier. Events flowing in from RudderStack into Courier are visible on the RudderStack integration page inside Courier Studio, where you can map them to trigger notification workflows. For instance, you can trigger an automation that welcomes a new user if an `identify` event flows in from RudderStack. **Supported RudderStack Events:** * Group * Identify * Track ### Configuration Go to RudderStack destination and search for Courier. Search Courier Destination Connect one or more sources Connect Sources to Courier Destination Add Courier API Key Add Courier API Key ### Event Trigger You can trigger events either via the sources you connected or by directly calling RudderStack endpoints. Courier destination supports `group`, `identify` and `track` events, which appear on the Courier RudderStack integration page inside Courier Studio UI. Check the [RudderStack Node SDK documentation](https://www.rudderstack.com/docs/sources/event-streams/sdks/rudderstack-node-sdk/#sending-events) to learn more about sending events to your RudderStack instance. Once events start flowing in from RudderStack into Courier, they appear on the RudderStack integration page in Courier Studio. Courier Inbound from RudderStack ### Supported Events #### Identify RudderStack identify events create or update the user profile in Courier. The `userId` is used as the user identifier and `traits` are mapped to user profile attributes. #### Track RudderStack track events can trigger an automation or supply an inline automations payload. The `properties` in the payload are used for mapping to Courier. #### Group RudderStack group events can create an account or trigger an automation. The `groupId` is used as Account ID and `userId` (or `anonymousId` if userId is not present) is used as the User Identifier. ### Event Mapping Received RudderStack events can be mapped to an existing automation, or a new automation can be initialized where the event is set as a trigger. Click on the plus (+) icon under link automations and map the event to an existing automation template, or create a new automation template. Properties should be scoped by the `courier.automation` object. For instance, if you want to map userId to refs, your request to RudderStack would look like this: ```json Rudderstack Courier Automation Property Example theme={null} { "event": "user-checkout", "type": "track", "properties": { "courier": { "automation": { "data": { "userId": "my-user" } } } } } ``` Mapped automation would look like this: RudderStack Mapped Automation V2 [Learn more about Courier Automations >](/docs/tutorials/automations/how-to-automate-message-sequences) *** ## Courier to RudderStack Courier generates events during workflow execution (for instance, a "message sent" event when a message is delivered). These events can be sent outbound to RudderStack, which can further unlock other use cases and wire up other destinations inside RudderStack. ### Configuration Add a source in RudderStack that will receive events from Courier (for example, a Node.js source). Once the source is created, copy the write key and data plane URL to the Courier RudderStack integration page. RudderStack Source Config Courier RudderStack Source Config ### Event Sourcing Courier will start sending generated events (like Message Sent) to your RudderStack instance. These should show up on your RudderStack source page. Courier to RudderStack Events *** ## Example Use Cases * User sync from RudderStack to Courier * Welcoming new users based on sign-up tracking * Nudging users to upgrade to a paid tier based on usage tracking # Segment Source: https://www.courier.com/docs/external-integrations/cdp/segment Connect Segment to Courier to send identify, track, and group events into Courier automations, and send Courier message events back to Segment for analytics. ## Segment to Courier Courier's Destination for Segment provides a straightforward way to send data from web or mobile applications into Courier to improve notification delivery and management. These events are visible on the [Segment integration page](https://app.courier.com/channels/segment) inside Courier Studio, where you can map them to trigger notification workflows. For example, you can trigger an automation that welcomes a new user when an `analytics.identify` event is received from Segment. **Supported Segment Events:** * `analytics.group` * `analytics.identify` * `analytics.track` ### Connecting Segment 1. Log into the Segment app and navigate to the [Destinations](https://segment.com/docs/connections/destinations) catalog page. 2. Click **Add Destination**. 3. Search for Courier in the Destinations Catalog and select the Courier destination. 4. Choose the Source that should send data to the Courier destination. 5. In the [Courier Integrations Page](https://app.courier.com/integrations), search for Segment and click on it to access the Courier API Key. If you don't see a key here, go to the [Courier Settings Page](https://app.courier.com/settings) and copy the Auth Token from the API Keys section. 6. Enter the Courier API Key or Auth Token in the API Key field of the Courier destination settings in Segment. 7. To validate the setup, navigate to the destination's "Event Tester" in Segment and click "Send Event" at the bottom of the page. In Courier Studio, refresh the page or wait a few seconds. If the destination setup was successful, the test event will appear in the list. ### Identify Calls Segment Identify calls allow you to connect a user to their actions (Segment events) and record traits about them. The user can be identified by their User ID and can hold additional traits such as their name and email. These traits can be used to update Courier recipient profiles over time. See the [Segment Identify spec](https://segment.com/docs/connections/spec/identify/) and [Courier Profiles API](/docs/api-reference/user-profiles/get-a-profile) for details. Example Payload ```json theme={null} { "messageId": "segment-test-message-iskh4123", "timestamp": "2024-05-21T18:00:18.913Z", "type": "identify", "email": "test@example.org", "traits": { "trait1": 2, "email": "test@example.org" }, "userId": "rod-test" } ``` ### Track Calls Segment Track calls allow you to record actions performed by your users, including any properties associated with their actions. See the [Segment Track spec](https://segment.com/docs/connections/spec/track/) for details. Track events appear with a prefix of `track/` in Courier. Courier gathers data from the track's `properties` object. To send notifications based on a Segment event, these track events must be mapped to Courier Automations. Here's an example Segment API call: ```jsx theme={null} analytics.track('Login Button Clicked', { messageId: "segment-test-message-a8rmf", timestamp: "2021-12-07T08:41:59.410Z", type: "track", email: "test@example.org", projectId: "4GgKeBoVJkT9EZL4vAmduv", properties: { property1: 1, property2: "test", property3: true }, userId: "test-user-cqw3gr", event: "Segment Test Event Name" }) ``` The associated JSON from the Segment API call will be sent to Courier as a track event: ```json theme={null} { "messageId": "segment-test-message-a8rmf", "timestamp": "2021-12-07T08:41:59.410Z", "type": "track", "email": "test@example.org", "projectId": "4GgKeBoVJkT9EZL4vAmduv", "properties": { "property1": 1, "property2": "test", "property3": true }, "userId": "test-user-cqw3gr", "event": "Segment Test Event Name" } ``` The above JSON object is mapped into the Courier data object as follows: ```json theme={null} { "data": { "property1": 1, "property2": "test", "property3": true } } ``` ### Troubleshooting If you continue to see the "No Segment events received yet." message in Courier, it could mean that Segment was unable to successfully set up your Courier workspace as a destination. To ensure that the test event sent by Segment is successfully received by Courier: 1. Double-check that your API Key was copied accurately into Segment. 2. Check where the `email` property is placed within the test JSON object in Segment. If it is at the top level of an identify call, move it within the `traits` object and resend the test event. 3. If you are still experiencing issues, please reach out to Courier Support. *** ## Courier to Segment You can also configure Courier as a Segment Source so that Courier message and audience events flow back into Segment for analytics. ### Setting Up Courier as a Segment Source 1. In Segment, navigate to the *Sources* page (under "Connections"). 2. Select your framework of choice (e.g., Node.js). 3. Obtain a write key. After adding Node.js as a source, you'll see a page with instructions on how to add Segment to your Node.js codebase. Copy the write key from this page. 4. In Courier's Segment configuration, paste your [Segment Write Key](https://segment.com/docs/connections/find-writekey/) in the *Segment write key* input field. After adding your Segment write key, Courier will send updates to Segment. Update events include messages being sent, opened, clicked, delivered, unroutable, and undeliverable. ### Courier Events Sent to Segment When Courier is set up as a Segment source, the following events can be tracked in Segment: #### Message Events * `Message Clicked` * `Message Delivered` * `Message Opened` * `Message Sent` * `Message Undeliverable` * `Message Unroutable` #### Audience Events * `Audience User Matched` * `Audience User Unmatched` #### Preferences Events * `User Subscribed` * `User Unsubscribed` # Chat API Direct Message Integration Source: https://www.courier.com/docs/external-integrations/direct-message/chat Integrate Chat API with Courier to send direct messages by phone or chat ID. Supports per-send credential overrides via providers.chat-api.override. The Chat API service (`chat-api.com`) may no longer be actively maintained. Verify the service status before integrating. ## Setup You will need a [Chat API](https://chat-api.com/) account with an instance configured. In Courier, navigate to the [Chat API Integration](https://app.courier.com/integrations/catalog/chat-api) page, enter your instance ID and token, then click "Save." ## Profile Requirements To deliver a message to a recipient over Chat API, Courier must be provided a phone number or chat ID. This value should be included in the recipient profile as `chat_api`. ```json theme={null} { "message": { "to": { "chat_api": { "phone_number": "12345678" } } } } ``` ```json theme={null} { "message": { "to": { "chat_id": "recipient-chat-id" } } } ``` ## Template In the notification's integration settings, you can provide a quoted message ID and mentioned phone numbers. ## Overrides You can use a provider override to change the request body or swap credentials at send time. ### Body Overrides Override any of the fields supported by Chat API's `/sendMessage` endpoint. ### Config Overrides You can swap the instance ID, token, or API URL at send time: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "chat_api": { "phone_number": "12345678" } }, "data": { "name": "Erika Mustermann" }, "providers": { "chat-api": { "override": { "config": { "instanceId": "RUNTIME_INSTANCE_ID", "token": "RUNTIME_TOKEN" } } } } } } ``` # Discord Source: https://www.courier.com/docs/external-integrations/direct-message/discord Send notifications to Discord users and channels via a Discord bot, with support for embeds, attachments, and provider overrides. ## Setup To send notifications via Discord, you need a Discord bot. You can use an existing bot or [create a new one](https://discord.com/developers/docs/intro). In Courier, navigate to the [Discord Integration](https://app.courier.com/integrations/catalog/discord) page, enter your bot token, then click "Save." ### Scopes Update the `bot` scope with the following permissions: * `View Channels` * `Send Messages` * Optional: `Read Message History` to send a message as a reply to another message [Learn more about Adding Scopes and Permissions.](https://discord.com/developers/docs/getting-started#adding-scopes-and-permissions) Once the permissions are finalized, go to the generated URL below. This URL will invite the bot to the server and authorize it with the permissions chosen. ## Profile Requirements The information required in the recipient profile is different based on the type of message you are sending. ### Sending a Direct Message To send a message to a user, you'll need to supply the Discord profile object with a `user_id`: 1. Go to User Settings on Discord (next to profile on the bottom left), 2. Access the Advanced settings page and enable Developer Mode , 3. Right click on the user and copy the user ID. This is not the user tag. The user you are trying to message must be a member of a server the bot is installed in. ```json theme={null} { "message": { // Recipient Profile "to": { "discord": { "user_id": "617099137532932107" } } } } ``` ### Sending a Message to a Channel To send a message to a channel, you'll need to supply the discord profile object with a `channel_id`: 1. Go to User Settings on Discord (next to profile on the bottom left), 2. Access the Advanced settings page and enable Developer Mode , 3. Right click on the channel and copy the channel ID. The bot must be installed in the server to send to the channel. ```json theme={null} { "message": { // Recipient Profile "to": { "discord": { "channel_id": "768866348853383208" } } } } ``` ## Overrides You can use a provider override to replace what Courier sends to Discord's [Create Message](https://discord.com/developers/docs/resources/channel#create-message) endpoint. For example, you can add an embed. ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "discord": { "channel_id": "768866348853383208" } }, "providers": { "discord": { "override": { "body": { "embed": { "title": "Hello, Embed!", "description": "This is an embedded message." } } } } } } } ``` # Facebook Messenger Integration Source: https://www.courier.com/docs/external-integrations/direct-message/facebook-messenger Send Courier notifications via Facebook Messenger. Provide facebookPSID in the recipient profile and use overrides to customize the body or config. ## Setup You will need a [Facebook Page](https://www.facebook.com/pages/create) with Messenger enabled and a Facebook App with the Messenger platform configured. In Courier, navigate to the [Facebook Messenger Integration](https://app.courier.com/integrations/catalog/facebook-messenger) page, enter your Page Access Token, then click "Save." ## Profile Requirements To deliver a message to a recipient over Facebook Messenger, Courier must be provided the recipient's Page-Scoped ID (PSID). The value should be included in the recipient profile as `facebookPSID`. ```json theme={null} { "message": { "to": { "facebookPSID": "1254477777772919" } } } ``` ## Overrides ### Body Overrides You can override any of the fields in the request body that Courier sends to the Messenger [Send API](https://developers.facebook.com/docs/messenger-platform/reference/send-api/). ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "facebookPSID": "1254477777772919" }, "providers": { "facebook-messenger": { "override": { "body": { "messaging_type": "UPDATE" } } } } } } ``` ### Config Overrides You can swap the Page Access Token or API URL at send time: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "facebookPSID": "1254477777772919" }, "providers": { "facebook-messenger": { "override": { "config": { "access_token": "RUNTIME_PAGE_ACCESS_TOKEN" } } } } } } ``` # Chat & Direct Message Providers Source: https://www.courier.com/docs/external-integrations/direct-message/intro-to-direct-message Overview of Courier's chat and direct message integrations, including provider directory, profile targeting, and provider-level overrides. Courier integrates with chat and direct message platforms to deliver notifications to users in the apps they already use. Each provider requires specific profile fields to identify the recipient, such as a Slack user ID, Discord channel ID, or phone number. ## Available Providers | Provider | Description | | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | | [Slack](/docs/external-integrations/direct-message/slack) | Workspace messaging with Block Kit, threading, webhooks, and slash command support | | [Microsoft Teams](/docs/external-integrations/direct-message/microsoft-teams) | Enterprise messaging via Teams apps and bots with Adaptive Card support | | [Discord](/docs/external-integrations/direct-message/discord) | Send direct messages or channel messages via a Discord bot | | [WhatsApp](/docs/external-integrations/direct-message/whatsapp) | WhatsApp messaging via Twilio with template verification | | [Facebook Messenger](/docs/external-integrations/direct-message/facebook-messenger) | Messenger delivery using Page-Scoped IDs | | [Viber](/docs/external-integrations/direct-message/viber) | Direct messages via a Viber bot account | | [Stream Chat](/docs/external-integrations/direct-message/stream-chat) | In-app messaging via the Stream Chat API | | [Chat API](/docs/external-integrations/direct-message/chat) | WhatsApp and other chat delivery via the Chat API service | Can't find a provider? Send us a chat or email [support@courier.com](mailto:support@courier.com) ## Provider Overrides Overrides let you modify parts of a message at send time without changing your notification template. They are passed in the `message` payload of a [Send request](/docs/api-reference/send/send-a-message) and applied just before Courier hands the message off to the provider. Provider overrides (`message.providers..override`) target a single provider and can pass through fields specific to that provider's API. Most chat providers support: * **`body`** overrides to change the message content, add embeds, blocks, or other provider-specific payload fields. * **`config`** overrides to swap credentials or endpoint URLs at send time. Each provider page documents its supported override schema. # Microsoft Teams Integration Source: https://www.courier.com/docs/external-integrations/direct-message/microsoft-teams Send notifications to Microsoft Teams via Courier. Create an Azure App Registration and Teams bot, then send Adaptive Card messages to channels or users. ## Overview This guide provides step-by-step instructions for creating and configuring a Microsoft Teams Application with an associated Bot. The Bot enables Courier to send notifications to Microsoft Teams channels and users. ## Prerequisites * Access to [Microsoft Teams Developer Portal](https://dev.teams.microsoft.com/) * [Azure portal](https://portal.azure.com/) access with App Registration permissions * Administrator privileges for granting API permissions * [Courier workspace](https://app.courier.com/) access ## Understanding the Microsoft Teams Integration Architecture Microsoft Teams integration requires **three separate but connected components**: 1. **Azure App Registration** - The parent application that manages authentication 2. **Teams App** - The application that gets installed in Teams 3. **Teams Bot** - The bot component that actually sends messages **Credential Flow:** * You'll create **two apps** (one in Azure, one in Teams) * You'll use **one set of credentials** in Courier (the bot's Azure credentials) * The Teams app links to the first Azure app, and the bot links to the Teams app ## Step-by-Step Setup 1. Navigate to the [Microsoft Teams Developer Portal](https://dev.teams.microsoft.com/). 2. Click **Create a new app**. 3. Enter a name for your app and click **Add**. We recommend adding `_app` to your app name to differentiate it clearly from your bot, especially if you manage multiple bots or apps. 4. Save the generated **App ID**—you'll need this later. 1. Open the [Azure Portal](https://portal.azure.com/). 2. Navigate to **Azure Active Directory > App registrations**. 3. Click **New registration**. 4. Configure the registration: * **Name**: Use the same name as your Teams Developer Portal app * **Supported account types**: Select **"Accounts in any organizational directory (Any Microsoft Entra ID tenant - Multitenant)"**. The Azure default is single-tenant, which will cause 401 authentication errors with Bot Framework. * **Redirect URI**: Keep the default setting 5. Click **Register**. 6. Save your **Application (client) ID**—you'll need this later. 1. Return to the [Microsoft Teams Developer Portal](https://dev.teams.microsoft.com/). 2. Navigate to **Configure > Basic Information**. 3. Scroll to the bottom of the page to the **Application (client) ID** field. 4. Paste the Application (client) ID you saved from Step 2. 5. Click **Save**. The Application (client) ID you enter here is from your Azure App Registration (Step 2). This is different from the credentials you'll use in Courier (Step 5), which come from the Bot's Azure App Registration (Step 4). 1. In the [Microsoft Teams Developer Portal](https://dev.teams.microsoft.com/), navigate to **Tools > Bot Management**. 2. Click **+ New Bot** and provide a unique name. Use a distinct name for your bot to avoid confusion with your app. 3. Return to **Apps > \[Your App Name] > Configure > App Features** 4. Click the **Bot** item 5. Select **Select an existing bot** 6. Choose the bot you just created 7. Configure the bot settings: * Under **What can your bot do?**, select: * ✓ Only send notifications (one-way conversations) * Under **Select the scopes where people can use your bot**, select: * ✓ Personal (for 1:1 notifications) * ✓ Team (for channel notifications) * ✓ Group Chat (for group chat notifications) 8. Click **Save** 9. **Configure API Permissions** (requires admin): * Return to the [Azure Portal](https://portal.azure.com/). * Navigate to **App registrations > \[Your Bot Name] > API permissions** * Add the following Microsoft Graph permissions as **Application permissions**: * `ChannelSettings.Read.All` * `TeamSettings.Read.All` * `User.Read.All` * Request administrator approval by having them click **Grant admin consent for \[your\_domain]** 10. **Generate Bot Credentials**: * In Azure, navigate to **App registrations > \[Your Bot Name] > Certificates & secrets** * Click **+ New client secret** * Provide a descriptive name and select an expiration period * Click **Create** * **Important**: Copy and securely store the generated secret value immediately * Navigate to the **Overview** section * Copy and store the **Application (client) ID** alongside your secret Now that your Microsoft Teams app is ready, configure it in Courier: 1. Head to the [Teams Integration](https://app.courier.com/integrations/catalog/msteams) in Courier. 2. Enter your **Application (client) ID** from Azure and **Client Secret** from Azure, both from Step 4. 3. Click **Install Provider**. Courier's integration requires the Application (client) ID and Client Secret from your Azure Bot App Registration (Step 4), not from the Teams Developer Portal. You can find these at: * [Azure Portal](https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/RegisteredApps) > **App registrations > \[Your Bot Name]**: *"Application (client) ID"* * [Azure Portal](https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/RegisteredApps) > **App registrations > \[Your Bot Name] > Certificates & secrets**: *"Client secrets"* **Install the App in Teams:** 1. Return to [Apps > your app](https://dev.teams.microsoft.com/apps) 2. Click **Publish**. When prompted, select **"Download the app package"** * **Note:** Ensure that you have filled in both short and long app descriptions within the **Configure > Basic information** section, as they are required fields in the app manifest. Your app will fail to install in Teams if these fields are left empty. 3. Go to [Microsoft Teams](https://teams.microsoft.com/) 4. Navigate to **Apps** and select **"Manage your apps"** at the bottom 5. Click **"Upload an app"** and select your downloaded app package 6. When prompted, click **"Add"** For channel messaging, the app must also be installed in each specific team whose channels you want to message. Uploading the app to Teams only enables personal (1:1) messaging. To install in a team: 1. In Microsoft Teams, navigate to the target team 2. Click the **"..."** (ellipsis) next to the team name and select **"Manage team"** 3. Go to the **"Apps"** tab 4. Find your app and click **"Add"** Without this step, channel messages will fail with a 401 authentication error even if your credentials are correct. 7. Courier is now capable of sending messages to channels or members of your Teams **Get Channel Identifiers (for testing):** 1. Enter [Microsoft Teams](https://teams.microsoft.com/) 2. Hover over one of your channels. You should see an ellipsis icon you can click on. This will expose a menu item called **"Copy link"**. Click this option. 3. You will see a URL similar to: `https://teams.microsoft.com/l/channel/19%3A5140d7460868414cac958ac76a0a94d0%40thread.skype/slack-teams-test?groupId=feb55fc1-9e00-40f3-93b8-f7d14703f4dd&tenantId=dabd1935-56a4-4305-938e-0840e2e84515` 4. Paste this string into a URL decoder, such as this one: [https://meyerweb.com/eric/tools/dencoder/](https://meyerweb.com/eric/tools/dencoder/) 5. Copy the **group ID**. This is the identifier of the team. For example: `feb55fc1-9e00-40f3-93b8-f7d14703f4dd` 6. Copy the **channel name**. In this example, it will be `slack-teams-test` 7. Copy the **tenant ID**. In this example, it will be `dabd1935-56a4-4305-938e-0840e2e84515` **Send Test Message:** 1. Return to [Courier](https://app.courier.com/assets/templates) and create a new template. 2. Select the Teams integration provider we created earlier in Step 5 3. In the **"Design"** tab, create a basic text message. Refer to our [Content Documentation](/docs/platform/content/content-overview) if you need to learn how to use the designer. 4. Click on the **Preview** tab. Create a **"Test event"**, which is a request you can use to send a message. 5. Click **"Create test event"** and enter the following (make sure to use your own field values): ```json TestEvent theme={null} { "courier": {}, "data": {}, "profile": { "ms_teams": { "team_id": "feb55fc1-9e00-40f3-93b8-f7d14703f4dd", "channel_name": "slack-teams-test", "tenant_id": "dabd1935-56a4-4305-938e-0840e2e84515", "service_url": "https://smba.trafficmanager.net/amer" } }, "override": {}, "meta": {} } ``` 6. Click **"Publish"** 7. Navigate to the **Send** tab. Click **"Send Test"** 8. Success! Congratulations! You've successfully created a Microsoft Teams Bot and configured it in Courier. You can now send notifications to Microsoft Teams channels and users. ## Profile Requirements To send notifications to Microsoft Teams, Courier requires the recipient's user profile to include an `ms_teams` object. This object must contain the following fields: * `tenant_id`: Your Microsoft Teams tenant ID. * `service_url`: The service URL for your region (e.g., `https://smba.trafficmanager.net/amer`). * One of the following identifiers: * `user_id` * `email` * `conversation_id` * Combination of `team_id` and `channel_name` * Thread reply fields: `reply_to_activity_id` and `conversation_id` ```json user_id theme={null} { "message": { "to": { "ms_teams": { "user_id": "", "tenant_id": "", "service_url": "https://smba.trafficmanager.net/amer" } } } } ``` ```json email theme={null} { "message": { "to": { "ms_teams": { "email": "", "tenant_id": "", "service_url": "https://smba.trafficmanager.net/amer" } } } } ``` ```json conversation_id theme={null} { "message": { "to": { "ms_teams": { "conversation_id": "", "tenant_id": "", "service_url": "https://smba.trafficmanager.net/amer" } } } } ``` ```json team_id+channel_name theme={null} { "message": { "to": { "ms_teams": { "team_id": "", "channel_name": "", "tenant_id": "", "service_url": "https://smba.trafficmanager.net/amer" } } } } ``` ```json thread_reply theme={null} { "message": { "to": { "ms_teams": { "reply_to_activity_id": "", "conversation_id": "", "tenant_id": "", "service_url": "https://smba.trafficmanager.net/amer" } } } } ``` To find your `tenant_id`, navigate to [https://teams.microsoft.com/?tenantId](https://teams.microsoft.com/?tenantId) and copy the `tenantId` query parameter from the redirected URL. If the parameter isn't visible, click the three-dot menu next to your Team, select **Get link to team**, and locate the `tenantId` in the URL. Locate Tenant ID in Microsoft Teams For users in the Americas region, the standard service URL is `https://smba.trafficmanager.net/amer`. To send messages using either `email` or `channel_name`, your bot must have the following Microsoft Graph API permissions: * `ChannelSettings.Read.All` (requires admin consent) * `TeamSettings.Read.All` (requires admin consent) * `User.Read.All` These permissions allow Courier to resolve the `user_id` or `channel_id` using the Microsoft Graph API. ### Using a tenant\_id `tenant_id` now conditionally required based on operation type. Now only required for the following operations: * send directly to a user when `user_id` is provided * send directly to a user when `email` is provided * send to a channel when only a channel name is provided, requiring lookup of channel id from channel name `tenant_id` extraction from service\_url path segments (can be either specified on service\_url, or as a body param, or both (but only if they are the same value) ```bash theme={null} service_url: https://smba.trafficmanager.net/amer/{tenant_id} tenant_id: {tenant_id} ``` `tenant_id` is only placed on `provider:sent` and `provider:delivered` event payloads if it is included in the request, as that is the only time it will be known. You should be able to respond to messages even if tenant\_id is not known -- it is not needed for this action. ## Thread Replies Courier's Microsoft Teams integration supports thread replies, allowing messages to be posted as replies to existing messages in channels rather than as new root-level messages. When Courier sends a message to Microsoft Teams, the Bot Framework returns reference data including an `activityId` and `conversationId` in the `provider:sent` event. This reference data can be used to reply to that specific message in a threaded conversation. ### Thread Reply Overview **The Flow:** 1. Send a root-level message to any Teams channel 2. Look up the sent event (via API, webhook, or Courier UI) to get the `activityId` and `conversationId` 3. Use those identifiers to send channel thread replies **Important Notes:** * **Channel Threading Only**: Thread replies are only supported in Teams channels, not in personal (1:1) conversations or group chats due to Microsoft Teams platform limitations. * **Works in All Channel Types**: Thread replies work in standard channels, private channels, and shared channels. * **Reference Data Required**: You need the `activityId` and `conversationId` from a previous message's `provider:sent` event. * **Threading Validation**: Courier validates thread reply configurations and provides clear error messages for invalid field combinations. **Example Workflow** 1. **Send Initial Message:** ```json theme={null} { "message": { "to": { "ms_teams": { "tenant_id": "your-tenant-id", "service_url": "https://smba.trafficmanager.net/amer", "team_id": "feb55fc1-9e00-40f3-93b8-f7d14703f4dd", "channel_name": "general" } }, "content": { "title": "Investigation Started", "body": "We're looking into the reported issue." } } } ``` 2. **Get Reference Data from provider:sent Event:** The `provider:sent` event contains the reference data you need. You can access this through: * **Courier API**: Query message details to get the `provider:sent` event data * **Courier UI**: View message details in the Messages section of your dashboard * **Webhooks** (if configured): Receive reference data automatically when messages are sent Example reference data from the `provider:sent` event: ```json theme={null} { "reference": { "activityId": "1756945821955", "conversationId": "19:a52e21710de34c65b9f2e09ededaad2a@thread.skype" } } ``` 3. **Send Thread Reply:** ```json theme={null} { "message": { "to": { "ms_teams": { "tenant_id": "your-tenant-id", "service_url": "https://smba.trafficmanager.net/amer", "reply_to_activity_id": "1756945821955", "conversation_id": "19:a52e21710de34c65b9f2e09ededaad2a@thread.skype" } }, "content": { "title": "Update", "body": "Issue has been identified and fix is being deployed." } } } ``` ### Thread Reply Configuration **Getting Reference Data** Reference data is available through multiple channels: 1. **Courier API** Query message details using Courier's API to retrieve the reference data from the message logs. 2. **Courier UI (Messages Log)** Navigate to your Courier dashboard and view the message details in the Messages section. The `provider:sent` log will contain the reference data. 3. **Webhook Delivery (Optional)** If you have webhooks configured, Courier automatically delivers reference data when messages are sent: ```json theme={null} { "type": "message:updated", "data": { "id": "1-61f9dd53-b5c6969eb23c4aad6fce2ef7", "status": "SENT", "providers": { "msteams": { "reference": { "activityId": "1756945821955", "conversationId": "19:a52e21710de34c65b9f2e09ededaad2a@thread.skype" } } } } } ``` **Thread Reply Requirements** For thread replies to work properly: 1. **Required Fields:** * `reply_to_activity_id`: The activity ID from the original message's reference data * `conversation_id`: The conversation ID from the original message's reference data * `tenant_id`: Your Microsoft Teams tenant ID * `service_url`: Your Teams service URL (optional, uses default if not provided) 2. **Field Restrictions:** When using `reply_to_activity_id`, you **cannot** include: * `channel_id` or `channel_name` + `team_id` (channel targeting fields) * `user_id` or `email` (user targeting fields) The `conversation_id` determines the thread location automatically. **Error Handling** Common threading errors and their solutions: * **"Thread replies require 'conversation\_id'"**: Include the `conversation_id` from the original message's reference data. * **"Thread replies cannot use channel targeting fields"**: Remove `channel_id`, `channel_name`, and `team_id` when using `reply_to_activity_id`. * **"Thread replies cannot use user targeting fields"**: Remove `user_id` and `email` when using `reply_to_activity_id`. When using threading fields, do not include channel targeting (`team_id`, `channel_name`, `channel_id`) or user targeting (`user_id`, `email`) fields, as the `conversation_id` determines the message destination. ## Microsoft Teams Channel ID Reference When sending messages to a Teams channel through Courier, the `conversationId` returned in delivered event webhooks is the channel ID. They are the same value. ### Understanding the Relationship In Microsoft Teams Bot Framework, a channel's identifier serves as its conversation ID. When you send a message to a channel, you're posting to a conversation that represents that channel. The Bot Framework returns this conversation identifier in the response, which Courier surfaces as `conversation_context.conversationId` in delivered event webhooks. ### Sending an Initial Message ```sh theme={null} curl --request POST \ --url https://api.courier.com/send \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "message": { "to": { "ms_teams": { "team_id": "feb55fc1-9e00-40f3-93b8-f7d14703f4dd", "channel_name": "General", "tenant_id": "dabd1935-56a4-4305-938e-0840e2e84515", "service_url": "https://smba.trafficmanager.net/amer" } }, "content": { "title": "Investigation Started", "body": "Security team has initiated an investigation into the incident." } } }' ``` The delivered event webhook contains: ```json theme={null} { "id": "1759342992794", "conversation_context": { "activityId": "1759342992794", "conversationId": "19:a52e21710de34c65b9f2e09ededaad2a@thread.skype" } } ``` The `conversationId` value `19:a52e21710de34c65b9f2e09ededaad2a@thread.skype` is the channel ID for the "General" channel. ### Replying in a Thread To reply to this message, use both the `activityId` and `conversationId` from the webhook: ```sh theme={null} curl --request POST \ --url https://api.courier.com/send \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "message": { "to": { "ms_teams": { "reply_to_activity_id": "1759342992794", "conversation_id": "19:a52e21710de34c65b9f2e09ededaad2a@thread.skype", "tenant_id": "dabd1935-56a4-4305-938e-0840e2e84515", "service_url": "https://smba.trafficmanager.net/amer" } }, "content": { "title": "Update", "body": "Initial analysis shows no signs of compromise." } } }' ``` The reply's delivered event maintains the same `conversationId`: ```json theme={null} { "id": "1759343317452", "conversation_context": { "activityId": "1759343317452", "conversationId": "19:a52e21710de34c65b9f2e09ededaad2a@thread.skype" } } ``` Throughout an entire thread, the `conversationId` remains constant because it represents the channel itself. Only the `activityId` changes with each new message. ### Getting Channel IDs Directly from Teams You can obtain a channel ID directly from the Teams application without sending a message first. Navigate to the Teams web app at [https://teams.microsoft.com/v2/](https://teams.microsoft.com/v2/), select your target channel, and click the ellipsis (⋯) icon in the upper right corner. Choose "Get link to channel" to copy a URL like: ``` https://teams.microsoft.com/l/channel/19%3Aa52e21710de34c65b9f2e09ededaad2a%40thread.skype/General?groupId=feb55fc1-9e00-40f3-93b8-f7d14703f4dd&tenantId=dabd1935-56a4-4305-938e-0840e2e84515 ``` The channel ID appears after `/channel/` in URL-encoded format. Decode `19%3Aa52e21710de34c65b9f2e09ededaad2a%40thread.skype` to get `19:a52e21710de34c65b9f2e09ededaad2a@thread.skype`. ### Sending Directly with conversation\_id When you know the channel ID upfront, you can send messages using `conversation_id` instead of looking up the channel by name: ```sh theme={null} curl --request POST \ --url https://api.courier.com/send \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "message": { "to": { "ms_teams": { "conversation_id": "19:a52e21710de34c65b9f2e09ededaad2a@thread.skype", "tenant_id": "dabd1935-56a4-4305-938e-0840e2e84515", "service_url": "https://smba.trafficmanager.net/amer" } }, "content": { "title": "Direct Channel Message", "body": "Sent using the channel ID directly as conversation_id." } } }' ``` This approach skips the Graph API lookup for channel name resolution, making it more performant. The webhook still returns the same `conversationId` because it's already the channel ID. ### Technical Details Channel IDs in Microsoft Teams follow the format `19:{guid}@thread.skype` or `19:{guid}@thread.tacv2` depending on the Teams infrastructure version. These identifiers are stable and unique per channel within a tenant. When combined with `tenantId`, they globally identify a specific channel. The Bot Framework uses these channel IDs as conversation identifiers when posting to channels. Thread replies require both the `conversationId` (channel ID) and the `reply_to_activity_id` (the message being replied to). The URL construction for threaded replies uses a special format `{conversationId};messageid={activityId}`, but the stored `conversationId` in the response remains just the channel ID without any suffix. For more information on Microsoft Teams integration with Courier, see [https://www.courier.com/docs/external-integrations/direct-message/microsoft-teams](https://www.courier.com/docs/external-integrations/direct-message/microsoft-teams) ## Advanced Features ### Overrides Overrides allow you to change the Azure Bot's App ID and App Password directly within your Courier message payload: ```json theme={null} { "message": { [...], "providers": { "msteams": { "override": { "config": { "appId": "", "appPassword": "" } } } } } } ``` ### Adaptive Cards Courier supports Microsoft Teams [Adaptive Cards](https://adaptivecards.io/) through Jsonnet blocks within the Template Designer. Adaptive Cards let you create interactive, visually appealing notifications for your users in Microsoft Teams. #### Using Jsonnet Blocks Jsonnet blocks enable you to customize the appearance and functionality of Adaptive Cards sent through Microsoft Teams. Follow the steps below to create and send your first Adaptive Card: 1. In Courier's Template Designer, add a new Microsoft Teams channel. 2. Insert a Jsonnet block within your message. 3. Open the [Adaptive Cards Designer](https://adaptivecards.io/designer/) and select or create a card layout. 4. Copy the card JSON from the "Card Payload Editor" into your Courier Jsonnet block. #### Sending an Adaptive Card When sending your Adaptive Card, ensure your Courier message includes the necessary sample data from the Adaptive Cards Designer: ```json theme={null} { "message": { "to": { "user_id": "QUICKSTART_USER" }, "template": "TGAV18XGBAM565JHKFZY3SJYZB52", "data": { "title": "Publish Adaptive Card Schema", "description": "Now that we have defined the main rules and features of the format, we need to produce a schema and publish it to GitHub. The schema will be the starting point of our reference documentation.", "creator": { "name": "Matt Hidinger", "profileImage": "https://pbs.twimg.com/profile_images/3647943215/d7f12830b3c17a5a9e4afcc370e3a37e_400x400.jpeg" }, "createdUtc": "2017-02-14T06:08:39Z", "viewUrl": "https://adaptivecards.io", "properties": [ {"key": "Board", "value": "Adaptive Cards"}, {"key": "List", "value": "Backlog"}, {"key": "Assigned to", "value": "Matt Hidinger"}, {"key": "Due date", "value": "Not set"} ] } } } ``` Ensure your recipient profile includes a valid `ms_teams` object with the appropriate fields (e.g., `conversation_id`). Once sent, your Adaptive Card will appear in Microsoft Teams: Congratulations, you've successfully sent an Adaptive Card through Courier! #### Using @Mentions in Adaptive Cards To include mentions in Adaptive Cards, you'll need two key elements: * `username` within the Jsonnet block. * A corresponding `entities` object within the Adaptive Card JSON payload, including the Teams user ID of the mentioned individual. Here's a sample mention: ```json theme={null} { "msteams": { "entities": [ { "type": "mention", "text": "John Doe", "mentioned": { "id": "29:123124124124", "name": "John Doe" } } ] } } ``` Now you're ready to enhance your Microsoft Teams notifications with interactive Adaptive Cards and mentions. ## Troubleshooting ### 401 Authentication Error If you see `All Bot Framework authentication methods failed` with status code 401: 1. **App not installed in the team (channel messages only)**: Uploading the app to Teams enables personal messaging, but for channel messaging the app must be installed in each specific team. See Step 6 for instructions. This is the most common cause when user messages succeed but channel messages fail. 2. **Wrong credentials in Courier**: Courier requires the Application (client) ID and Client Secret from the Azure **Bot** App Registration (Step 4), not the Teams App ID from the Developer Portal (Step 1). These are different values. 3. **Single-tenant App Registration**: In Azure Portal > App registrations > \[Your Bot] > Authentication, ensure "Supported account types" is set to "Accounts in any organizational directory" (Multitenant). The Azure default is single-tenant, which breaks Bot Framework authentication. 4. **Expired client secret**: Navigate to Azure Portal > App registrations > \[Your Bot] > Certificates & secrets and check the expiration date. If expired, create a new secret and update it in Courier's Teams integration settings. 5. **Missing `tenant_id`**: Ensure your message profile includes `tenant_id` in the `ms_teams` object. Without it, tenant-scoped authentication cannot be attempted. ### Messages to Users Work but Channel Messages Fail This almost always means the app is not installed in the team that owns the target channel. See item 1 above and Step 6 for installation instructions. ### Credential Confusion Between Azure App Registrations The setup process creates two Azure App Registrations: * **Step 2 App Registration**: Used in the Teams Developer Portal configuration (Step 3). This is the parent application. * **Step 4 Bot App Registration**: Used in Courier's integration settings (Step 5). This provides the bot credentials. If you're unsure which credentials to use where, check the Info callouts in Steps 3 and 5. ## Reference: Courier → Recipient Flowchart ```mermaid theme={null} flowchart TB subgraph col1 [" "] Courier[("Courier (Notification Service)")] TeamsBot{{"Microsoft Teams Bot"}} end subgraph col2 [" "] BotService[["Bot Service (Bot Framework SDK) Deployed on Azure / Netlify / etc"]] TeamsApp{{"Microsoft Teams App"}} end subgraph col3 [" "] TeamsDestination[("Microsoft Teams Channel/Chat/User")] end Courier -- Send message Bot ID & Bot Client Secret --> TeamsBot TeamsBot -- Forward message to endpoint URL --> BotService BotService -- Send message App ID & App Password --> TeamsApp TeamsApp -- Deliver message --> TeamsDestination %% Invisible lines for layout Courier === TeamsBot TeamsBot === BotService BotService === TeamsApp TeamsApp === TeamsDestination style Courier fill:#803492,stroke:#333,color:#FFFFFF style BotService fill:#bbf,stroke:#333 style TeamsBot fill:#bfb,stroke:#333 style TeamsApp fill:#fbb,stroke:#333 style TeamsDestination fill:#464EB8,stroke:#333,color:#FFFFFF linkStyle 4 stroke:none,fill:none linkStyle 5 stroke:none,fill:none linkStyle 6 stroke:none,fill:none linkStyle 7 stroke:none,fill:none classDef default stroke-width:2px style col1 fill:none,stroke:none style col2 fill:none,stroke:none style col3 fill:none,stroke:none ``` # Slack Messaging Integration Source: https://www.courier.com/docs/external-integrations/direct-message/slack Send Slack messages with Courier by creating a Slack app, adding bot token scopes, and targeting users by email or channel ID. ## Prerequisites **To get started, you'll need:** * A Courier account ([Sign up](https://app.courier.com/signup)) * A Slack account ([Sign up](https://slack.com/get-started)) **What you'll do:** 1. Add the Slack integration in Courier 2. Create and configure a Slack app 3. Design a notification template 4. Send a test message * [Courier API Reference](/docs/reference/get-started) * [Slack API Documentation](https://api.slack.com/) * [Slack Block Kit Builder](https://app.slack.com/block-kit-builder/) *** ## Step-by-Step: Send Your First Slack Message Once logged in to Courier, go to the [Integrations page](https://app.courier.com/integrations) and select Slack. Click "Install" to add the integration. 1. Go to the [Slack Apps page](https://api.slack.com/apps) and click "Create an App". 2. Choose "From scratch", give your app a name, and select your development workspace. 3. Under "OAuth & Permissions", add these Bot Token Scopes: `chat:write`, `im:write`, `users:read`, `users:read.email`. 4. Click "Install App to Workspace" and authorize. 5. Copy the **Bot User OAuth Access Token** (starts with `xoxb-`). Slack OAuth Scopes Go to the Courier [Assets page](https://app.courier.com/assets/templates) and click + New > Message Template. Select Slack from your list of integrations. In the sidebar, click the newly added Slack block to open the Slack template editor. Add your desired message content to the template. Click Preview, then select Create Test Event. Enter your bot token in the Access Token field. Click Send—your message should appear in Slack! *** ## Message Targeting Reference Courier supports sending Slack messages to users and channels in several ways. Here are the most common targeting methods: ```json Direct Message by Email theme={null} { "message": { "to": { "slack": { "access_token": "xoxb-xxxxx", "email": "user@example.com" } } } } ``` ```json Direct Message by User ID theme={null} { "message": { "to": { "slack": { "access_token": "xoxb-xxxxx", "user_id": "UEFNTF6QL" } } } } ``` ```json Message a Channel theme={null} { "message": { "to": { "slack": { "access_token": "xoxb-xxxxx", "channel": "CL2MR6HEX" } } } } ``` Order of Precedence: If you provide more than one of channel, user\_id, or email, Courier will use them in this order: channel > user\_id > email. *** ## Overrides You can override the payload sent to Slack's [chat.postMessage](https://api.slack.com/methods/chat.postMessage) using `providers.slack.override.body`. This is useful for advanced formatting, interactivity, and threading. #### Unfurl Links ```json theme={null} { "providers": { "slack": { "override": { "body": { "unfurl_links": true } } } } } ``` #### Slack Blocks (Block Kit) Send rich, interactive layouts using [Slack blocks](https://api.slack.com/block-kit): ```json theme={null} { "providers": { "slack": { "override": { "body": { "blocks": [ { "type": "header", "text": { "type": "plain_text", "text": "Welcome!" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "This is a *section* block." } } ], "text": "Fallback plain text." } } } } } ``` Design and preview your blocks visually with the [Slack Block Kit Builder](https://app.slack.com/block-kit-builder/). #### Replying in a Thread To reply to a thread, set the `thread_ts` value: ```json theme={null} { "providers": { "slack": { "override": { "body": { "thread_ts": "1234567890.123456" } } } } } ``` ### Mentioning Users Mention a user in your message using `<@USER_ID>` syntax in your template: ``` Hello <@UEFNTF6QL>, you have a new notification! ``` You can also use variables for dynamic mentions. ### Preventing Unwanted Mentions Slack automatically parses text for `@name` patterns and turns them into mentions. If your message content includes text that resembles a mention but shouldn't trigger one, set [`verbatim: true`](https://docs.slack.dev/reference/block-kit/composition-objects/text-object/) on the Block Kit text object to disable this parsing. Courier's standard template blocks (text, quote, etc.) don't expose the `verbatim` flag. To use it, either pass raw Block Kit JSON via `providers.slack.override.body.blocks` or use a [Jsonnet block](/docs/platform/content/content-blocks/jsonnet-blocks) in the template designer: ```json theme={null} { "type": "section", "text": { "type": "mrkdwn", "text": ">>> Message from @jennifer about the deployment", "verbatim": true } } ``` ### Slash Command Responses If responding to a [Slash Command](https://api.slack.com/interactivity/slash-commands), use the `response_url` as an incoming webhook: ```json theme={null} { "to": { "slack": { "incoming_webhook": { "url": "https://hooks.slack.com/commands/1234/5678" } } } } ``` Set `override.slack.body.response_type` to `in_channel` or `ephemeral` as needed. ### Incoming Webhooks You can send messages to a channel using a Slack [Incoming Webhook](https://api.slack.com/messaging/webhooks): ```json theme={null} { "to": { "slack": { "incoming_webhook": { "url": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX" } } } } ``` ### Updating Notifications To update a previously sent Slack message, set a "replacement key" (usually `ts`) in your notification template's Slack channel settings. Courier will use this key to update the message instead of posting a new one. *** ## Tracking Delivery When Courier sends a Slack message via Bot OAuth (`chat.postMessage`), it captures Slack's `ts` (message timestamp identifier) and `channel` (conversation ID) from the API response. You can use these values to thread replies, update messages, or link back to the original Slack message. ### Where delivery data appears Slack-specific fields are returned in the `providers` array of the [GET /messages/](/docs/api-reference/sent-messages/get-message) response and in [`message:updated`](/docs/platform/workspaces/outbound-webhooks#messageupdated) webhook events: ```json theme={null} { "providers": [ { "provider": "slack", "status": "DELIVERED", "channel": { "key": "direct_message:slack", "name": "Slack", "template": "SFTYJKSF0241SVH2TWY97TTFFTQG" }, "reference": { "channel": "C04EXAMPLE", "ts": "1630512468.000700" }, "sent": 1630512468691, "delivered": 1630512468700 } ] } ``` | Field | Description | | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | `providers[].reference.ts` | Slack message timestamp. Use this to [reply in a thread](#replying-in-a-thread) or [update the message](#updating-notifications). | | `providers[].reference.channel` | Slack conversation ID where the message was posted. | | `providers[].channel` | Courier routing metadata (template and channel key); not the Slack channel ID. | Slack messages are marked as `DELIVERED` immediately after sending because Slack's API confirms delivery synchronously. The `delivered` and `sent` timestamps will be very close together. Incoming Webhook sends may not include `reference` data because Slack's Incoming Webhook API does not return `ts` or `channel` in its response. *** ## Troubleshooting * **Missing or incorrect Slack scopes:** * Double-check your app has all required scopes (`chat:write`, `im:write`, `users:read`, `users:read.email`, and `chat:write.public` for channels). * Reinstall your Slack app after updating scopes. * **Bot not invited to channel:** * Make sure your Slack app/bot is a member of the channel you want to message. * **Invalid or missing tokens:** * Ensure you are using the correct Bot User OAuth Access Token (starts with `xoxb-`). * Never use a user token or an expired token. * **Permission errors or message not delivered:** * Check the [Courier Message Logs](https://app.courier.com/logs/messages) for error details and troubleshooting tips. * **User or channel not found:** * Double-check the email, user\_id, or channel ID. For channels, copy the ID from the Slack URL. * **Message Truncated:** * Slack blocks [limit the characters](https://api.slack.com/reference/block-kit/blocks#section) in a single section to 3k characters. Courier automatically truncates Slack messages over 3k characters by removing escape and formating characters that are added by Slack after submitting the block. If you're still stuck, reach out to Courier support at [support@courier.com](mailto:support@courier.com). # Stream Chat Integration Source: https://www.courier.com/docs/external-integrations/direct-message/stream-chat Send messages to Stream Chat channels via Courier. Supply channelType and channelId or messageId in the streamChat profile; override credentials per send. ## Setup You will need a [Stream Chat](https://getstream.io/chat/) account with a project configured. In Courier, navigate to the [Stream Chat Integration](https://app.courier.com/integrations/catalog/stream-chat) page, enter your API key, API secret, and sender ID, then click "Save." ## Profile Requirements To deliver a message to a recipient over Stream Chat, Courier must be provided a `channelType` and `channelId`, or a `messageId` (for updating an existing message). Include these in the recipient profile as `streamChat`. ```json theme={null} { "message": { "to": { "streamChat": { "channelType": "messaging", "channelId": "my-channel-id" } } } } ``` ```json theme={null} { "message": { "to": { "streamChat": { "messageId": "my-message-id" } } } } ``` ## Overrides You can override the request body, API credentials, and endpoint URL that Courier sends to Stream Chat. * Override any of the fields supported by Stream Chat's `POST /channels/{type}/{id}/message` API endpoint. [See all send request body fields here.](https://getstream.io/chat/docs/rest/#/product%3Achat/SendMessage) * Override `apiKey`, `apiSecret`, and `senderId` via the config object. * Override `baseUrl` (defaults to `https://chat-us-east-1.stream-io-api.com`). ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "streamChat": { "channelType": "messaging", "channelId": "my-channel-id" } }, "data": { "name": "Foo Bar" }, "providers": { "stream-chat": { "override": { "body": { "skip_push": true }, "config": { "baseUrl": "https://custom-stream-endpoint.example.com", "apiKey": "RUNTIME_API_KEY", "apiSecret": "RUNTIME_API_SECRET", "senderId": "RUNTIME_SENDER_ID" } } } } } } ``` # Viber Messaging Integration Source: https://www.courier.com/docs/external-integrations/direct-message/viber Send Viber direct messages via Courier. Register a webhooks server, collect UserIDs, set viber.receiver in the profile, and override auth or message type. ## Setup You will need: 1. A Viber [bot account](https://partners.viber.com/account/create-bot-account). Make a note of the auth token. 2. An active webhooks server that can receive POST requests from Viber. Viber provides a [Node.js utility](https://www.npmjs.com/package/viber-bot) for handling these requests. In Courier, navigate to the [Viber Integration](https://app.courier.com/integrations/catalog/viber) page, enter your auth token and sender name, then click "Save." ### Webhooks Server Before sending notifications, Viber requires a webhooks server to be registered: ```bash theme={null} curl -X POST 'https://chatapi.viber.com/pa/set_webhook' \ -H 'Content-Type: application/json' \ -H 'X-Viber-Auth-Token: VIBER_AUTH_TOKEN' \ -d '{ "url": "YOUR_WEBHOOK_SERVER_URL", "event_types": ["delivered"] }' ``` This server will receive events from Viber such as UserIDs of users who subscribe, as well as delivery status events. ## Profile Requirements To send a direct message to a user, supply the Viber `UserID` to the `viber.receiver` property of the recipient profile. The recipient must have an active [Viber](https://www.viber.com/en/) account. The `UserID` is sent to your webhooks server after a user messages the Viber bot. ```json theme={null} { "message": { "to": { "viber": { "receiver": "12943673==" } } } } ``` ## Overrides ### Body Overrides You can override the request body that Courier sends to the Viber [Send Message API](https://developers.viber.com/docs/api/rest-bot-api/#send-message). ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "viber": { "receiver": "12943673==" } }, "providers": { "viber": { "override": { "body": { "type": "picture", "media": "https://example.com/image.jpg" } } } } } } ``` ### Config Overrides You can swap the auth token, sender name, or API URL at send time: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "viber": { "receiver": "12943673==" } }, "providers": { "viber": { "override": { "config": { "token": "RUNTIME_AUTH_TOKEN", "name": "My Bot Name" } } } } } } ``` # WhatsApp Messaging Integration Source: https://www.courier.com/docs/external-integrations/direct-message/whatsapp Send WhatsApp messages through Courier via Twilio. Requires a pre-approved template, phone_number in the recipient profile, and a Twilio account. Courier uses the [Twilio API for WhatsApp](https://twilio.com/whatsapp) as the delivery partner. ## Setup You will need a [Twilio account](https://www.twilio.com/) with WhatsApp enabled. In Courier, navigate to the [WhatsApp Integration](https://app.courier.com/integrations/catalog/twilio-whatsapp) page, enter your Twilio Account SID, Auth Token, and the WhatsApp-enabled "From" number, then click "Save." ## Profile Requirements To deliver a message to a recipient over Twilio Whatsapp, Courier must be provided the recipient's SMS-compatible telephone number. This value should be included in the recipient profile as `phone_number`. ```json theme={null} { "message": { "to": { "phone_number": "+1555-555-5555" } } } ``` ## Notification Categories Whatsapp allows the following [notification categories](https://www.twilio.com/docs/whatsapp/tutorial/send-whatsapp-notification-messages-templates#whatsapp-notification-categories): * Marketing * Authentication * Utility Other types of categories will likely be rejected, including: * Account Update * Alert Update * Appointment Update * Auto-Reply * Issue Resolution * Payment Update * Personal Finance Update * Reservation Update * Shipping Update * Ticket Update * Transportation Update ## WhatsApp Template Verification When using WhatsApp templates with Twilio, it's essential to understand the verification process, as WhatsApp has stringent guidelines to ensure the quality and appropriateness of messages sent through their platform. ### Create the Template **Template Structure:** WhatsApp [message templates](https://www.twilio.com/docs/whatsapp/tutorial/send-whatsapp-notification-messages-templates) are predefined messages that can include placeholders for dynamic content. Templates can be of various types such as text, media (images, documents), or interactive messages (buttons, list messages). **Template Categories:** These are typically [categorized](#notification-categories) into use cases like transactional updates, customer service, or alerts. ### Submit the Template for Approval **Access Twilio Console:** Navigate to the Twilio Console and go to the Messaging section. **Create Template:** In the WhatsApp Templates section, create a new template. You'll need to provide details like the template name, category, language, and the message content. This content should be free of any promotional material as WhatsApp strictly disallows promotional content in templates. **Submit for Review:** Once you've filled in the necessary details, [submit](https://www.twilio.com/docs/whatsapp/tutorial/message-template-approvals-statuses) the template for WhatsApp's review. ### WhatsApp Approval Process **Review by WhatsApp:** After submission, the template goes through WhatsApp's review process. This usually takes a few minutes to up to 24 hours. WhatsApp reviews the template to ensure it complies with their policies. Possible Outcomes: * **Approved:** If the template meets WhatsApp's guidelines, it will be approved. You can now use this template to send messages via the Twilio API. * **Rejected:** If the template does not meet the guidelines, it will be [rejected](https://www.twilio.com/docs/whatsapp/tutorial/message-template-approvals-statuses#common-rejection-reasons). Reasons for rejection could include promotional content, inappropriate language, or violating other WhatsApp policies. You will need to modify the template and resubmit it for approval. ## Approved Templates with Courier **Integration with Courier:** Once your template is approved, you can use the [Send API](/docs/api-reference/send/send-a-message) to send messages using the Twilio approved template. You'll need to specify the [Twilio content SID](https://www.twilio.com/docs/whatsapp/tutorial/send-whatsapp-notification-messages-templates#creating-message-templates-and-submitting-them-for-approval) and paste it to a WhatsApp channel in Courier. WhatsApp Template **Sending Messages:** When sending a message using an approved template, you make an API call with the required details. Twilio handles the rest, ensuring the message is delivered to the recipient via WhatsApp. While Twilio defines the content of your notification as a `template`, the format of your notification is what's important for the verification process. Once it's approved, you can use [Courier Templates](/docs/platform/content/template-designer/template-designer-overview) as long as they follow the approved format design. ### Key Considerations **Template Content:** Ensure that the content of your templates is clear, concise, and non-promotional. Include all necessary placeholders and provide sample values to give context during the review process. **Localization:** If you need to send messages in multiple languages, you'll need to create and get approval for each language version of the template. **Monitoring and Updates:** Keep an eye on the [performance](https://help.twilio.com/articles/360039737753-Recommendations-and-best-practices-for-creating-WhatsApp-Message-Templates) of your templates. If users frequently report your messages as spam, it might affect your ability to send messages. You may need to periodically update your templates to remain compliant with any new WhatsApp guidelines. ## Overrides ### Body Overrides You can override the request body fields sent to the Twilio WhatsApp API. The `ContentSid` field is particularly useful for specifying a Twilio-approved template at send time: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "phone_number": "+15555555555" }, "providers": { "twilio-whatsapp": { "override": { "body": { "ContentSid": "HXXXXXXXXXXXXXXXXXXXXXXXXXXX" } } } } } } ``` ### Config Overrides You can swap Twilio credentials or the "From" number at send time: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "phone_number": "+15555555555" }, "providers": { "twilio-whatsapp": { "override": { "config": { "accountSid": "RUNTIME_ACCOUNT_SID", "authToken": "RUNTIME_AUTH_TOKEN", "from": "+14155551234" } } } } } } ``` # Amply Email Integration Source: https://www.courier.com/docs/external-integrations/email/amply Route email through Amply via Courier. Configure your access token, and use provider overrides to change the sender name or add attachments per send. ## Setup You will need a [Amply (SendAmply)](https://sendamply.com/) account. In Amply, create an Access Token from your account settings. In Courier, navigate to the [Amply Integration](https://app.courier.com/integrations/catalog/amply) page, enter your Access Token, From Email, and From Name, then click "Save." ## Profile Requirements To deliver a message to a recipient over Amply, Courier must be provided the recipient's email address. This value should be included in the recipient profile as `email`. ```json title=JSON theme={null} { "message": { // Recipient Profile "to": { "email": "alice@acme.com" } // ... rest of message definition } } ``` ## Overrides You can use a provider override to replace what we send to Amply's Mail Send API. For example, you can use the following payload to override the sender name: ```json title=JSON theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "amply": { "override": { "config": { "fromName": "Acme Notifications" } } } } } } ``` Everything inside of `message.providers.amply.override` will replace what we send to Amply's Messages API. You can see all the available options by visiting [Amply API docs](https://docs.sendamply.com/reference/mail-send). ## Sending Attachments To include an attachment in the email, you can use the following override: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "data": { "hello": "world" }, "providers": { "amply": { "override": { "attachments": [ { "filename": "billing.pdf", "contentType": "application/pdf", "data": "Q29uZ3JhdHVsYXRpb25zLCB5b3UgY2FuIGJhc2U2NCBkZWNvZGUh" } ] } } } } } ``` # AWS SES Email Integration Source: https://www.courier.com/docs/external-integrations/email/aws-ses Connect AWS SES to Courier via Access Keys or IAM Role. Verify sender emails, configure SES region and sandbox limits, and override content per message. ## Setting Up Email with AWS SES Before beginning this tutorial, ensure you have an AWS SES account. If you do not have an AWS SES account yet, please sign up: * [Sign up for AWS SES ](https://portal.aws.amazon.com/billing/signup#/start) ### Step 1: Add the AWS SES Integration to Courier Before choosing an authentication method: 1. Log in to Courier 2. Navigate to the [Integrations](https://app.courier.com/integrations) page 3. Select the [AWS SES Integration](https://app.courier.com/integrations/aws-ses) to configure it ### Authentication Methods AWS SES integration in Courier supports two authentication methods: 1. AWS Access Keys 2. AWS IAM Role (Cross-Account Trust) Choose one of the following authentication methods to get started: ### Method 1: AWS Access Keys * Create an AWS SES API Key: 1. Log in to AWS SES 2. Navigate to "Settings" → "My Security Credentials" 3. Go to "Access management" → "Users" 4. On the "Users" page, select "Add user" and follow the steps to create a new IAM user with `AmazonSESFullAccess` permissions Be sure to download and save the Access Key ID and Secret Access Key upon user creation. If your use case requires specific permissions beyond the `AmazonSESFullAccess` policy, you may need to create a custom IAM policy: 1. In the AWS IAM console, navigate to "Policies" and select “Create policy.” 2. Use the JSON editor to define the policy. Here's an example of a custom policy allowing specific SES actions: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": ["ses:SendRawEmail", "ses:GetSendStatistics"], "Resource": "*" } ] } ``` 3. After defining the policy, attach it to the IAM user created for AWS SES. * Integrate AWS SES API Key with Courier: After creating the IAM user and obtaining the API keys, add them to the Courier AWS SES integration page. ### Method 2: AWS IAM Role (Cross-Account Trust) * Configure the minimum required IAM policy for sending emails: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ses:SendEmail", "ses:SendRawEmail" ], "Resource": [ "arn:aws:ses:region:${YOUR_AWS_ACCOUNT_ID}:identity/example.com", "arn:aws:ses:region:${YOUR_AWS_ACCOUNT_ID}:identity/specific@example.com" ] } ] } ``` * Create an IAM Role with the following trust policy: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "464962053586" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "${YOUR_COURIER_WORKSPACE_ID}" } } } ] } ``` * Users will need to add `/test` to the end of the `ExternalId` value that you set in the role policy you create in your AWS instance for working in **Courier's test environment**. * After creating the role, copy its ARN and paste it in the "Role ARN" field in the Courier AWS SES integration settings. When using IAM Role authentication, you'll need to replace the following placeholders: * `${YOUR_AWS_ACCOUNT_ID}`: Your AWS account ID * `${YOUR_COURIER_WORKSPACE_ID}`: Your Courier workspace ID ## Complete Your AWS SES Setup After configuring your chosen authentication method, complete the following steps: ### Step 1: Add a Verified "From" Address in Courier 1. Add a verified email address (e.g., [support@example.com](mailto:support@example.com)) to the "From Address" field in Courier. * The "From" email address you set will be used for all emails sent via the AWS SES integration. You can [override this default "From" address](/docs/platform/content/template-settings/email-fields#setting-the-reply-to-cc-and-bcc-addresses) on a per channel basis within your templates. 2. Ensure the "From" address is a verified identity in your AWS-SES account. 3. For more information on verifying identities, see [Verifying an Identity for Amazon SES Sending Authorization ](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/sending-authorization-identity-owner-tasks-verification.html). New AWS SES accounts start in a limited state called the "SES sandbox." In this mode, you can only send emails to verified addresses and domains. To enable email sending to non-verified addresses, you must request AWS to lift these restrictions. For guidance on this process, see [Moving out of the Amazon SES sandbox](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/request-production-access.html). ### Step 2: Configure AWS SES Region Select your preferred AWS SES region from the dropdown menu in the Courier AWS SES integration. ### Step 3: Create and Send a Courier Notification using AWS SES Refer to [Create and Send a Message](/docs/platform/sending/send-message) for instructions on building your notification template and sending a message with the Courier API using cURL. ## Profile Requirements for Message Delivery To deliver a message via AWS SES, include the recipient's email address in the profile as shown below: ```json theme={null} { "message": { // Recipient Profile "to": { "email": "alice@acme.com" } // ... rest of message definition } } ``` ## AWS SES Delivery Tracking Unlike polling-based providers (SendGrid, Postmark, Mailgun), AWS SES requires you to configure SNS webhooks for Courier to track delivery status. Without this setup, messages sent via SES will stay at "Sent" in your [Message Logs](/docs/platform/analytics/message-logs) indefinitely. See [How to Debug Email Delivery Issues](/docs/tutorials/monitoring/how-to-debug-delivery-issues) for more on troubleshooting delivery. To enable delivery tracking for emails sent via AWS SES, you need to set up Amazon SNS topics and configure AWS SES to publish delivery notifications to these topics. Flow: ```markdown theme={null} Email Sent via AWS SES → AWS SES publishes event to SNS Topic → SNS forwards to Courier webhook → Courier updates message status → Your webhook receives notification with MessageId ``` ### Setup Instructions You'll need your Courier Message Events webhook URL for AWS SES. Via Courier App: * Navigate to Channels → Email → AWS SES provider * Look for "Message Events Webhook URL" section * Copy the webhook URL `Important: Keep this URL secure - it authenticates webhooks from AWS to Courier.` Configuration Sets in AWS SES enable event publishing. * Open the AWS Console and navigate to Amazon SES * Click Configuration Sets in the left sidebar * Click Create Configuration Set * Enter a name (e.g., `courier-delivery-tracking`) * Click Create * Click on your newly created Configuration Set * Navigate to the Event destinations tab * Click Add destination * Select Amazon SNS as the destination type * Configure the destination: * Event types: Select `Bounce`, `Delivery`, and `Reject` * SNS Topic: * Choose Create new SNS topic if you don't have one * Or select an existing topic * Topic Name (if creating new): `courier-ses-delivery-events` * Click `Next` and then `Add destination` * Navigate to Amazon SNS in AWS Console * Click `Topics` in the left sidebar * Find and click the topic you created/selected in Step 3 * Click `Create subscription` * Configure the subscription: * Protocol: `Select HTTPS` * Endpoint: Paste your Courier webhook URL from Step 1 * Enable raw message delivery: `UNCHECKED` (very important!) * Click `Create subscription` **Automatic Confirmation:** AWS will send a subscription confirmation request to Courier. Courier automatically confirms the subscription - no action needed on your part. Wait 30 seconds, then refresh the page to see the subscription status change to "Confirmed". ### Assign Configuration Set to Your Identity Assign the Configuration Set as the default for your verified email address or domain. This ensures all emails sent from that identity automatically use the Configuration Set for delivery tracking. **For a Verified Email Address:** * In AWS SES Console, navigate to Verified identities * Click on your verified email address (e.g., [noreply@yourdomain.com](mailto:noreply@yourdomain.com)) * Go to the Configuration set tab * Click Edit * Select your Configuration Set (courier-delivery-tracking) * Click Save changes **For a Verified Domain:** * In AWS SES Console, navigate to Verified identities * Click on your verified domain (e.g., yourdomain.com) * Go to the Configuration set tab * Click Edit * Select your Configuration Set (courier-delivery-tracking) * Click Save changes **Grant the Configuration Set in your IAM policy.** Once a Configuration Set is set as the default on an identity, AWS SES authorizes `ses:SendRawEmail` against **both** the identity **and** the Configuration Set resource. If your IAM policy (Method 1 or Method 2 above) scopes `Resource` to specific ARNs and does not include the Configuration Set, sends will start failing with: ``` User `arn:aws:sts:::assumed-role/customer-ses-role/...` is not authorized to perform `ses:SendRawEmail` on resource `arn:aws:ses:us-east-1::configuration-set/courier-delivery-tracking` ``` Add the Configuration Set ARN to the `Resource` list of the policy attached to the IAM user (Method 1) or the assumed role (Method 2): ```json theme={null} "Resource": [ "arn:aws:ses:region:${YOUR_AWS_ACCOUNT_ID}:identity/example.com", "arn:aws:ses:region:${YOUR_AWS_ACCOUNT_ID}:configuration-set/courier-delivery-tracking" ] ``` If your policy uses `"Resource": "*"`, no change is needed. After updating, sends recover automatically on the next retry. ## Overrides Overrides in Courier offer the flexibility to customize settings specifically for AWS SES, adapting to more advanced email sending requirements. This is useful when you need to pass custom configurations, such as a MIME 1.0 string, instead of using the standard Courier template. While using Courier's override feature, you can modify various fields that are specific to AWS SES's `SendRawEmail` method. For a comprehensive list of fields that can be overridden in the context of AWS SES, refer to the AWS documentation on the [SendRawEmail API](https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html). ### Using Overrides to Modify Email Settings Here’s an example of how to implement an override in Courier to modify the RawMessage data: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "erika.mustermann@example.com" }, "data": { "name": "Erika Mustermann" }, "providers": { "aws-ses": { "override": { "body": { "RawMessage": { "Data": "" } }, "config": { "accessKeyId": "", "secretAccessKey": "" } } } } } } ``` ### Sending Attachments with Overrides To include an attachment in your email, use the `attachments` override as follows: ```json theme={null} { "message": { "to": { "email": "erika.mustermann@example.com" }, "template": "NOTIFICATION_TEMPLATE_ID", "data": { "name": "Erika Mustermann" }, "providers": { "aws-ses": { "override": { "attachments": [ { "filename": "hello.txt", "contentType": "text/plain", "data": "SGk=" } ] } } } } } ``` ## Troubleshooting > Dealing with Amazon SES requests can result in some errors. You can find them below to help you troubleshoot. You can also check the [Courier Logs](/docs/platform/analytics/message-logs) to help debug any provider errors you may encounter. For anything else, you may contact [Courier Support](mailto:support@courier.com). ### Amazon SES 554 Error This error occurs due to numerous reasons. 1. It occurs when you have not verified the sender email (sender identity) on Amazon SES. 2. If you're using the Amazon SES in the sandbox environment and have not verified the recipient's email address, you may encounter this error. 3. You may encounter this error when you've provided an invalid recipient email address. #### Solution You can try the [following](https://docs.aws.amazon.com/ses/latest/dg/troubleshoot-smtp.html) steps mentioned below. * Open the Amazon SES console and verify that the sender email identity you are using has a verification status of `verified`. * If you're using the sandbox environment, ensure that you have added the recipient email address as a verified identity on the SES console. It is mandatory to add the recipient emails on Amazon SES when running in the Sandbox environment. * If both the sender and recipient email addresses are verified, ensure that you have provided the correct recipient email address for the "To" parameter. * If none of the above works for you, verify that the region specified in your AWS SDK is the same region that contains the verified identities. For example, if the verified identities are located in the Virginia region (us-east-1), you should initialize the Amazon SES instance in the same region. ### Amazon SES Email Address is Not Verified This error occurs if you try to send emails using an unverified identity in the region specified. Additionally, this error occurs when sending emails in the sandbox environment using unverified sender and recipient email identities. #### Solution You may try the following to [resolve](https://docs.aws.amazon.com/ses/latest/dg/troubleshoot-error-messages.html) the error. 1. **Verify the region** Verify that you are connected to SES in the region where all your verified identities are located. 1. **Confirm identity verification** Confirm that the sender's identity has been verified. If you are using the sandbox environment, confirm the verification status of the recipient identities as well. To do so, visit the SES Console and navigate to your verified identities. The status of the identities should be marked as "Verified" as shown below. If the status for your identity is `Unverified` you will have to verify the identity before sending the email. 1. **Verify email addresses** If the identities have been verified, ensure that the email addresses you have provided are correctly spelled. ### AWS SES Timeout This error occurs when the client (such as an EC2) cannot establish a TCP connection to the public endpoint of Amazon SES. Generally, this is caused when the client (EC2) has a firewall to block outgoing connections on the SMTP ports (25, 587, or 465) or if the client does not have access to an internet connection. #### Solution To resolve the error, ensure that the client has an active/stable internet connection. Hereafter, update the firewall rules on the client to allow outgoing connections on ports 25, 587, and 465 (depending on the port you use). ### AWS SES BCC Not Working This error occurs when the recipient email in the "TO" field is present in the "BCC" field. Certain email providers do not allow the email to contain duplicate recipients. Additionally, this error may occur if the email address in the "BCC" field does not exist. #### Solution To resolve the error, ensure that the recipient's email address is not the same as the `BCC` email address. If the email addresses in the `BCC` are unique, verify the validity of the email addresses specified in the "BCC" list. ### Error: is not authorized to perform ses sendemail This error occurs when an AWS service such as a Lambda function is not authorized to send an email using Amazon SES. #### Solution To resolve the error, you will need to attach a policy to the IAM role to allow the AWS resource to execute the `ses:SendEmail` action. For example, if you wish to provide a Lambda function the permission to send an email using SES, you would have to create and attach an inline policy for the function's IAM role that allows the `ses:SendEmail` action. The inline policy is shown below. ```json theme={null} { "Version": "2012-10-17", "Statement" : [ { "Sid": "Inline Policy for SES Send Email", "Effect": "Allow", "Resource" : "*", "Actions":[ "ses:SendEmail" ] } ] } ``` The inline policy shown above will ensure that the AWS service is allowed to execute the `SendEmail` action on an Amazon SES resource and will resolve the permission error. ### Amazon SES Authentication Credentials Invalid This error occurs when the SMTP username and password provided to connect to the SMTP endpoint of Amazon SES are incorrect. #### Solution 1. **Verify credentials:** Ensure that the username and password you enter are correct and the same one SES provided. 2. **Verify the region:** SMTP credentials in Amazon SES differ per region. Therefore, ensure that the credentials used are associated with your region. 3. **Use SMTP credentials and not console credentials:** * It is important to note that the credentials used in the SMTP endpoint are not the same as those you use for AWS. It would help if you had the Amazon SES SMTP credentials to access the Amazon SES SMTP interface. * You will have to create an IAM user that can invoke the SES services and generate SMTP credentials for the newly created IAM user. It can be done using the SES console. * First, navigate to your SES account dashboard. You will see a section titled - "SMTP Settings." Under this, you should see the output shown below. Click "Create SMTP Credentials." This will direct you to the IAM Console, where you will be prompted to create an IAM User with the policies required to invoke SES. Afterward, click "Create." This will create the IAM User, generate the credentials, and display the output below. To resolve the error, you can download the generated credentials and provide these values for the SMTP username/password. ### Amazon SES 530 Authentication Required This error occurs when the SMTP credentials provided to Amazon SES are invalid. This can be the username, password, port, and endpoint. Additionally, this error may occur if you have not used TLS. #### Solution You can try the following to see which one fixes the error. 1. **Verify credentials:** Ensure that the SMTP username and password you provide are the same credentials you created for the IAM User with permissions to invoke SES. 2. **Verify the region:** Verify that you connect to SES in the region where all your verified identities are located. 3. **Verify SMTP configurations:** Visit the SES console and navigate to your account dashboard. In the account dashboard, you should see the SMTP configurations for SES. Cross-check the SMTP configurations shown in the SES console with the endpoint and the port you've provided to ensure that SES has been configured correctly. 1. **Use the correct port:** Ensure that the port used is port - 587. Some users have experienced issues using the TLS Wrapper port and found that using port 587 (TLS port) fixes the error. ### AWS SES Rate Limit Amazon SES has a limit of **one email per second** in the sandbox environment. However, you can exceed this rate for a short period, not for long periods. #### Solution To [resolve this error](https://docs.aws.amazon.com/ses/latest/dg/manage-sending-quotas.html), contact AWS and request production access for SES. Your request will be reviewed, and based on your use case, AWS will grant a reasonable email rate for your SES account. Later on, you can increase this rate by contacting AWS. ### AWS SES Email Not Received This error occurs if the templated email is missing a handlebar parameter. For example, if the email template requires five handlebar parameters and you've specified only four, Amazon SES will send the email and will not display any error. However, the email will not get delivered to the recipient, causing this. It may be possible to debug by viewing the [Courier logs](/docs/platform/analytics/message-logs) for any rendering errors. #### Solution To resolve the error, verify that all the required handlebar parameters have been added to the templated email parameters when sending the email. # Gmail Email Integration Source: https://www.courier.com/docs/external-integrations/email/gmail Send email through Gmail in Courier via OAuth. Suited for testing only due to API rate limits. Covers fixing 400 invalid_grant errors by re-authorizing. Gmail is intended for testing and small-scale internal sends only. Google enforces daily send limits (500/day for consumer Gmail, 2,000/day for Workspace), may flag or suspend accounts sending bulk email via the Gmail API, and OAuth tokens require periodic re-authorization. For production email delivery, use a dedicated transactional provider like [SendGrid](/docs/external-integrations/email/sendgrid), [AWS SES](/docs/external-integrations/email/aws-ses), or [Postmark](/docs/external-integrations/email/postmark). ## Setup When you set up Gmail as a provider and connect your Gmail account, Courier will request permission to send emails on your behalf. Courier will not send any emails unless you explicitly make a send request using the provider. ### OAuth Authorization Google APIs use the OAuth protocol for authentication and authorization. Once given permission, Courier will request an access token from the Google Authorization Server, and send the token to the Google Gmail API on your behalf. To give Courier access to your Gmail credentials, you will need to consent to Courier's requested Gmail scopes by signing into your desired Gmail inbox and allowing Courier permission. ### Updating Authorized Account On the [Gmail Integration](https://app.courier.com/integrations/gmail) page, you can click on "Authorize a different Gmail inbox" to send messages from a different account. This will require you to give permissions for the new email account every time you change the account. ## Profile Requirements To deliver a message to a recipient over Gmail, Courier must be provided the recipient's email address. This value should be included in the recipient profile as `email`. ```json theme={null} { "message": { // Recipient Profile "to": { "email": "alice@acme.com" } // ... rest of message definition } } ``` ## Overrides Gmail does not support provider-level body overrides. OAuth authorization and token refresh are handled automatically by Courier. ## Troubleshooting ### All Gmail sends failing with "Request failed with status code 400" This error means Courier's stored OAuth refresh token is no longer valid. Google rejected the token refresh with a `400 invalid_grant` response, so all subsequent sends fail as `UNDELIVERABLE`. **Common causes:** * The Gmail account password was changed * A Google Workspace admin revoked Courier's OAuth access * Google revoked the token due to inactivity or a security review * The Google Cloud project's OAuth consent screen is in "Testing" mode (tokens expire after 7 days) **How to confirm:** In Courier, check the message timeline for a Gmail-routed message. If you see `UNDELIVERABLE` with the error `"Request failed with status code 400"` and `willRetry: false`, the refresh token is dead. **Fix:** Go to **Channels → Gmail** in Courier Studio, click **Update** next to the authorized account, and re-authorize with your Google account. This issues a fresh access token and refresh token, restoring sends immediately. If the Gmail channel is configured with routing fallback, messages that fail on Gmail will fall through to the next configured channel (e.g., Courier Inbox). Only the Gmail delivery path is affected. # Email Providers Overview Source: https://www.courier.com/docs/external-integrations/email/intro-to-email Connect Courier to 13 email providers: SendGrid, SES, Mailgun, and more. Override subject, From, CC/BCC, HTML body, attachments, and open tracking. Courier integrates with a wide range of email providers. To send an email, the recipient profile must include an `email` field: ```json theme={null} { "message": { "to": { "email": "alice@acme.com" }, "template": "NOTIFICATION_TEMPLATE_ID" } } ``` When a notification template includes an email channel, Courier selects the configured email provider based on your [channel priority](/docs/platform/sending/channel-priority) and routing rules. If the primary provider fails, Courier automatically [fails over](/docs/platform/sending/failover) to backup providers when configured. ## Available Email Providers | Provider | Description | | --------------------------------------------------------------- | ---------------------------------------------------------- | | [Amazon SES](/docs/external-integrations/email/aws-ses) | AWS-hosted sending with IAM role or access key auth | | [SendGrid](/docs/external-integrations/email/sendgrid) | Twilio SendGrid with template import and delivery tracking | | [Postmark](/docs/external-integrations/email/postmark) | Transactional email with MessageStream support | | [Mailgun](/docs/external-integrations/email/mailgun) | Email delivery with webhooks and EU region support | | [Resend](/docs/external-integrations/email/resend) | Modern email API with tagging and attachments | | [SMTP](/docs/external-integrations/email/smtp) | Generic SMTP relay via NodeMailer | | [Mandrill](/docs/external-integrations/email/mandrill) | Mailchimp Transactional with template import | | [SparkPost](/docs/external-integrations/email/sparkpost) | High-volume email delivery | | [Mailjet](/docs/external-integrations/email/mailjet) | Transactional and marketing email | | [MailerSend](/docs/external-integrations/email/mailersend) | Domain-verified transactional email | | [Amply](/docs/external-integrations/email/amply) | Email delivery with attachment support | | [Gmail](/docs/external-integrations/email/gmail) | OAuth-based sending for testing and small-scale use | | [OneSignal Email](/docs/external-integrations/email/onesignal-email) | Email via OneSignal's notification platform | ## Email Channel Overrides Overrides let you modify parts of an email at send time from within the [Send request](/docs/api-reference/send/send-a-message) payload. They work with both template-based and [inline (Elemental)](/docs/platform/content/elemental/elemental-overview) sends, and are applied just before Courier hands the message off to the provider. There are two levels of override: * **Channel overrides** (`message.channels.email.override`) apply to every email provider configured for the send. Use these when you want to change the subject, from address, HTML body, or add attachments regardless of which provider sends the email. * **Provider overrides** (`message.providers..override`) target a single provider and can pass through fields specific to that provider's API. Each provider page documents its supported override schema. Channel overrides and provider overrides can be used together. If both set the same field, the provider override takes precedence. Overrides are applied **after** the render step in the notification lifecycle. This means the Rendered tab in the Courier logs **will not reflect** overrides; it shows the pre-override output. To verify the final payload, check the provider request in the Raw tab. ### Available Channel Override Fields The table below is the complete list of fields you can set in `message.channels.email.override`. All fields are optional; include only the ones you need. | Field | Type | Description | | ------------- | ------ | -------------------------------------------------------------------------------------------------------------------------- | | `subject` | string | Overrides the email subject line. | | `from` | string | Sender address. Accepts a plain email or `"Display Name "` format. | | `reply_to` | string | Sets the Reply-To header. Same format as `from`. | | `cc` | string | CC recipients as a comma-separated string (e.g. `"a@example.com,b@example.com"`). | | `bcc` | string | BCC recipients as a comma-separated string. Courier converts this to an array for providers that require it. | | `html` | string | Replaces the rendered HTML body entirely. | | `text` | string | Replaces the plain-text body. | | `attachments` | array | File attachments. Schema varies by provider; see provider pages for details. | | `brand` | object | Overrides brand settings (logo, colors). Uses the same schema as [POST /brands](/docs/api-reference/brands/create-a-new-brand). | | `tracking` | object | Controls tracking behavior. Set `{ "open": false }` to disable open tracking. | Here is an example Send request with every channel override field populated: ```json theme={null} { "message": { // ...rest of request "channels": { "email": { "override": { "subject": "Your order has shipped", "from": "Acme Notifications ", "reply_to": "support@acme.com", "cc": "manager@acme.com,team-lead@acme.com", "bcc": "audit@acme.com,records@acme.com", "html": "

Order #1234 shipped

", "text": "Order #1234 has shipped. Track it at https://acme.com/track/1234", "attachments": [ { "filename": "receipt.pdf", "contentType": "application/pdf", "data": "base64-encoded-content" } ], "brand": { "settings": { "email": { "header": { "barColor": "#674ea7" } } } }, "tracking": { "open": false } } } } } } ``` ## Brand Override The `channels.email.override.brand` property uses same schema as the API request payload for the [POST /brands](/docs/api-reference/brands/create-a-new-brand) endpoint. Brand elements that can be overridden: * logo * top bar color * brand colors **Example:** ```json theme={null} { "message": { // ...rest of request "channels": { "email": { "override": { "brand": { "settings": { "email": { "header": { "logo": { "image": "https://www.courier.com/logo.png", "href": "https://www.courier.com" }, "barColor": "#674ea7" } } } } } } } } } ``` ## Allowlist for AWS IP Addresses Some email providers, such as Mailgun, offer [additional security](https://help.mailgun.com/hc/en-us/articles/360012244474-IP-Allowlist) to allowlist IP addresses to access their API. Courier is hosted on AWS and does not provide an IP range in the form of an allowlist. As a workaround, users can subscribe to the `AmazonIpSpaceChanged` topic, and receive notifications about any changes to the AWS IP address ranges. For details, refer to the [AWS documentation](https://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html#subscribe-notifications). # MailerSend Email Integration Source: https://www.courier.com/docs/external-integrations/email/mailersend Integrate MailerSend with Courier: verify your domain, generate an API token, set a From Address, and override sender or add attachments per send. ## Setup You will need a [MailerSend](https://www.mailersend.com/) account with a verified sending domain. In MailerSend, add your domain and complete DNS verification. Click "Manage" on your domain and generate an API token. In Courier, navigate to the [MailerSend Integration](https://app.courier.com/integrations/catalog/mailersend) page. Enter your API token and From Address, then click "Save." ## Profile Requirements To deliver a message to a recipient over MailerSend, Courier must be provided the recipient's email address. This value should be included in the recipient profile as `email`. ```json theme={null} { "message": { "to": { "email": "alice@acme.com" } // ... rest of message definition } } ``` ## Overrides You can override any of the fields supported by MailerSend's [/v1/email endpoint](https://developers.mailersend.com/api/v1/email.html#send-an-email). Overrides are useful when a field is not yet supported by Courier or you want to replace a value that Courier generates. **Config override example:** ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "mailersend": { "override": { "config": { "apiKey": "", "fromAddress": "alternate-sender@yourdomain.com" } } } } } } ``` ### Attachments To include an attachment, add an `attachments` array in the override. File content must be base64-encoded. ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "mailersend": { "override": { "attachments": [ { "filename": "billing.pdf", "contentType": "application/pdf", "data": "Q29uZ3JhdHVsYXRpb25zLCB5b3UgY2FuIGJhc2U2NCBkZWNvZGUh" } ] } } } } } ``` ## Troubleshooting A [422 error](https://www.mailersend.com/help/how-to-start-sending-emails#rest-api) from MailerSend can have several causes: | Error | Cause | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | From email must be verified | The domain of the from email address must match the domain that the API token is from. | | Must provide HTML/text or template ID | The API request is missing content; provide either an HTML/text body or a template ID. | | File type not supported | The attachment is not a [supported file type](https://developers.mailersend.com/api/v1/email.html#supported-file-types). | | Reply-to must be a valid email | The `reply_to` parameter is not a valid email address. | | Email quota reached | The account's quota has been reached. Ensure your account is approved for production sending. | The most common cause is an unverified sending domain. Make sure your domain is [verified with MailerSend](https://www.mailersend.com/help/how-to-verify-and-authenticate-a-sending-domain) before using it with Courier. # Mailgun Source: https://www.courier.com/docs/external-integrations/email/mailgun Send emails with Mailgun via Courier. Covers profile setup, overrides, attachments, EU host config, delivery webhooks, and error handling. ## Setup You will need a [Mailgun](https://www.mailgun.com/) account with a verified sending domain. In Mailgun, navigate to your domain settings to get your API key and domain name. In Courier, navigate to the [Mailgun Integration](https://app.courier.com/integrations/catalog/mailgun) page, enter your API key, domain, and From Address, then click "Save." For EU-region Mailgun accounts, set the host to `api.eu.mailgun.net` in the integration settings or via a config override. ## Profile Requirements Courier uses the `email` key in the recipient profile to send messages through Mailgun. ```json theme={null} { "message": { "to": { "email": "user@example.com" }, // …other message properties } } ``` ## Overrides Use the `override` object to customize the payload sent to Mailgun’s Messages API. Example: add a Mailgun tag. ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "user@example.com" }, "providers": { "mailgun": { "override": { "body": { "o:tag": "notifications" }, "config": { "apiKey": "", "domain": "", "host": "" } } } } } } ``` Courier replaces the full request body with the contents of `override`. To customize the `fromAddress` or other Mailgun config options, set them under `override.config`. Refer to the [Mailgun API docs](https://documentation.mailgun.com/docs/mailgun/user-manual/sending-messages/) for supported parameters. To send through Mailgun’s EU region, set the `host` value to `api.eu.mailgun.net`. ```json theme={null} "config": { "apiKey": "", "domain": "", "host": "api.eu.mailgun.net" } ``` ## Attachments To include attachments, add an `attachments` array in the override. File content must be base64-encoded. ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "user@example.com" }, "data": { "hello": "world" }, "providers": { "mailgun": { "override": { "attachments": [ { "filename": "billing.pdf", "contentType": "application/pdf", "data": "Q29uZ3JhdHVsYXRpb25zLCB5b3UgY2FuIGJhc2U2NCBkZWNvZGUh" } ] } } } } } ``` ## IP Allowlisting Mailgun supports IP allowlists for API access. Courier runs on AWS and doesn’t use fixed outbound IPs. To manage this, AWS provides a workaround:\ Subscribe to the [`AmazonIpSpaceChanged`](https://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html#subscribe-notifications) SNS topic. You’ll get notified whenever AWS updates its IP ranges, so you can update your allowlist accordingly. ## Delivery Tracking Courier polls Mailgun for status by default. To get real-time delivery updates, set up Mailgun webhooks. In Courier, go to the [Mailgun configuration page](https://app.courier.com/integrations/catalog/mailgun). Copy the generated Webhook URL. In Mailgun, navigate to **Sending → Webhooks**.\ Add a webhook for **Delivered Messages**, and paste the URL.\ Repeat for **Permanent Failure**. In Mailgun, ensure the selected domain matches the one configured in Courier. After \~1 hour (to allow in-flight updates), disable polling in Courier under **Enable polling for status updates**, then click **Save**. ## Troubleshooting Mailgun returns a 550 error when the sending domain doesn’t have an MX record. **Fix:**\ Add an MX record to your domain’s DNS. Wait \~2 hours for propagation. Mailgun may throttle or suspend delivery for accounts with high bounce/spam rates or traffic spikes. **Fix:** 1. Complete Mailgun’s Business Verification. 2. Remove addresses that bounce consistently. Avoid bulk sends to unverified users. # Mailjet Email Integration Source: https://www.courier.com/docs/external-integrations/email/mailjet Send email via Mailjet using Courier. Configure public and private API keys, then use provider overrides to attach files or customize the Send API request. ## Setup You will need a [Mailjet](https://www.mailjet.com/) account. In Mailjet, navigate to [API Key Management](https://app.mailjet.com/account/apikeys) to get your Public Key and Private Key. In Courier, navigate to the [Mailjet Integration](https://app.courier.com/integrations/catalog/mailjet) page, enter both keys and your From Address, then click "Save." ## Profile Requirements To deliver a message to a recipient over Mailjet, Courier must be provided the recipient's email address. This value should be included in the recipient profile as `email`. ```json theme={null} { "message": { // Recipient Profile "to": { "email": "alice@acme.com" } // ... rest of message definition } } ``` ## Overrides You can use a provider override to replace what we send to Mailjet's Send API. For example, you can add an attachment to your request: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "mailjet": { "override": { "body": { "Attachments": [ { "ContentType": "text/plain", "Filename": "test.txt", "content": "VGhpcyBpcyB5b3VyIGF0dGFjaGVkIGZpbGUhISEK" } ] } } } } } } ``` Everything inside of `message.providers.mailjet.override` will replace what we send to Mailjet's Send API. You can see all the available options by visiting [Mailjet API docs](https://dev.mailjet.com/email/guides/send-api-V3/). # Mandrill Email Integration Source: https://www.courier.com/docs/external-integrations/email/mandrill Send email via Mandrill (Mailchimp Transactional) using Courier. Import templates, apply overrides for attachments, and fix BCC, CC, and merge-var issues. ## Setup You will need a [Mailchimp Transactional (Mandrill)](https://mandrillapp.com/) account. In Mandrill, navigate to Settings and create an API key. In Courier, navigate to the [Mandrill Integration](https://app.courier.com/integrations/catalog/mandrill) page, enter your API key, From Address, and From Name, then click "Save." ## Profile Requirements To deliver a message to a recipient over Mandrill, Courier must be provided the recipient's email address. This value should be included in the recipient profile as `email`. ```json theme={null} { "message": { // Recipient Profile "to": { "email": "alice@acme.com" } // ... rest of message definition } } ``` ## Overrides You can use a provider override to replace what we send to Mandrill's Messages API. For example, you can add an attachment to your request: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "mandrill": { "override": { "body": { "message": { "from_email": "me@courier.com", "subject": "Hi there", "from_name": "Rod", "attachments": [ { "type": "text/plain", "name": "myfile.txt", "content": "ZXhhbXBsZSBmaWxl" } ] } } } } } } } ``` Everything inside of `messageproviders.mandrill.override` will replace what we send to Mandrill's Messages API. You can see all the available options by visiting [Mandrill API docs](https://mandrillapp.com/api/docs/messages.JSON.html). ## Template Import You can import your Mandrill templates to use with Courier from the [Mandrill configuration page](https://app.courier.com/channels/mandrill). You will need to provide your Mandrill credentials in the configuration page to retrieve your saved templates from Mandrill. Templates ready for import will appear as selectable checkboxes that you can choose to import. ## Troubleshooting > Dealing with Mandrill requests can result in some errors. You can find them below to help you troubleshoot. You can also check the [Courier Logs](/docs/platform/analytics/message-logs) to help debug any provider errors you may encounter. For anything else, you may contact [Courier Support](mailto:support@courier.com). ### Mandrill Click Tracking Not Working The three most possible causes are that you have not enabled click tracking, the URL is too long, so Mandrill has disabled click-tracking, or the clicks are recorded but not updated in real-time. **Solution** * Verify whether click tracking is enabled. 1. Login to the Mandrill account. 2. Settings -> Sending defaults. 3. Make sure your choice from the "Track Clicks" dropdown is something other than the "No click tracking" option. Even though the opens and clicks get tracked in real-time, the status updates may delay from a few minutes to a more extended period. For instance, this may happen due to the load on their system. [Courier provides its own link tracking](/docs/platform/workspaces/workspaces-overview) which you can use to track clicks on your notification templates. ### Mandrill BCC Not Working Most possibly, Mandrill is ignoring the BCC headers and hence this error occurs. **Solution** 1. Try using the `to` field rather than the `bcc` field and set `X-MC-PreserveRecipients` to `false`. 2. Or, specify the `bcc` address in the `to` field but declare their `type` as `bcc`. Add `preserve_recipients: true` under the message section. The code given below is an example of implementing this solution. ```json theme={null} { "to":[ { "email":"to1.email@example.com", "name":"To Recipient Name", "type":"to" }, { "email":"bcc1.email.@example.com", "name":"BCC1 Recipient Name", "type":"bcc" }, { "email":"bcc2.email@example.com", "name":"BCC2 Recipient Name", "type":"bcc" } ] } ``` You can read more about X-MC-PreserveRecipients [here](https://mailchimp.com/developer/transactional/docs/smtp-integration/#customize-messages-with-smtp-headers) ### Mandrill CC Emails Not Present CC email fields passed to Mandrill's API can be done [through the designer](/docs/platform/content/template-settings/email-fields), or through an API override definition. Similar to BCC emails not being respected, `preserve_recipients` needs to be set to `true` in the override schema request so that CC emails can be passed to Mandrill's API. If this field isn't set to `true`, **CC emails will not be sent**. **Solution** ```json theme={null} { // ... rest of message definition "providers":{ "mandrill":{ "override":{ "body":{ "message":{ "preserve_recipients":true, //This setting must be set to true when passing CC recipients. "attachments":[ { "name":"Top Secret", "content":"ZXhhbXBsZSBmaWxl", "type":"application/pdf" } ] } } } } } } ``` ### Mandrill Merge Vars Not Working The cause for the Mandrill merge variables not working is possible because of an issue with the nesting. **Solution** You have to make sure the variables are nested in the `message` struct for it to work. Given below is an example of how it is done. ```javascript theme={null} var message = { to: "sample@gmail.com", mandrillOptions: { template_name: 'template1', template_content: [ ], message: { "merge": true, "merge_language": "handlebars", "global_merge_vars": [{ "name": "fname", "content": "Sample" }, { "name": "email", "content": "sample@gmail.com" } ] } } }; ``` # OneSignal Email Source: https://www.courier.com/docs/external-integrations/email/onesignal-email Integrate OneSignal Email with Courier by configuring your App ID and REST API Key, and sending email messages using the recipient's email address. ## Setup You will need a [OneSignal](https://onesignal.com/) account. Get your `App ID` and `REST API Key` by following [these instructions](https://documentation.onesignal.com/docs/accounts-and-keys). In Courier, navigate to the [OneSignal Email Integration](https://app.courier.com/integrations/catalog/onesignal-email) page, enter both values, then click "Save." ## Profile Requirements To deliver a message to a recipient over OneSignal Email, Courier must be provided the recipient's email address. This value should be included in the recipient profile as `email`. ```json theme={null} { "message": { "to": { "email": "alice@acme.com" } } } ``` The `oneSignalPlayerID` and `oneSignalExternalUserId` profile fields are used by the [OneSignal push provider](/docs/external-integrations/push/onesignal-push), not the OneSignal Email provider. ## Overrides OneSignal Email does not support provider-level overrides or attachments. # Postmark Email Integration Source: https://www.courier.com/docs/external-integrations/email/postmark Send transactional email via Postmark using Courier. Use overrides to set MessageStream, attach files, or call Postmark's Templates API with a TemplateId. ## Setup You will need a [Postmark](https://postmarkapp.com/) account with a verified sender signature. In Postmark, navigate to your server's API Tokens page to get your Server API Token. In Courier, navigate to the [Postmark Integration](https://app.courier.com/integrations/catalog/postmark) page, enter your API token and From Address, then click "Save." ## Profile Requirements To deliver a message to a recipient over Postmark, Courier must be provided the recipient's email address. This value should be included in the recipient profile as `email`. ```json theme={null} { "message": { // Recipient Profile "to": { "email": "alice@acme.com" } // ... rest of message definition } } ``` ## Subject Character Limit The Postmark API imposes a subject line limit of \~60 characters. Due to encoding, using emojis or special characters in the subject line will decrease this limit. ## Overrides You can use a provider override to replace what we send to Postmark's Email API. For example, you can add [MessageStream](https://postmarkapp.com/support/article/1207-how-to-create-and-send-through-message-streams) and an attachment to your request: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "postmark": { "override": { "config": { "MessageStream": "message_stream_id" }, "body": { "Attachments": [ { "Name": "readme.txt", "Content": "dGVzdCBjb250ZW50", "ContentType": "text/plain" } ] } } } } } } ``` The `message.providers.postmark.override` property can be used to replace what we send to Postmark's Email API. Note: the override does not replace the entire object, only the elements that you define in the body of the override. You can see all the available options by visiting the [Postmark API docs](https://postmarkapp.com/developer/api/email-api). ## Using Postmark Templates ```json theme={null} { "message": { "to": { "email": "rodrigo@courier.com" }, "template": "NOTIFICATION_TEMPLATE_ID", "providers": { "postmark": { "override": { "config": { "url": "https://api.postmarkapp.com/email/withTemplate" }, "body": { "TemplateId": "123456", "TemplateModel": { "product_url": "product_url_Value", "product_name": "product_name_Value", "name": "name_Value", "action_url": "action_url_Value", "login_url": "login_url_Value", "username": "username_Value", "trial_length": "trial_length_Value", "trial_start_date": "trial_start_date_Value", "trial_end_date": "trial_end_date_Value", "support_email": "support_email_Value", "live_chat_url": "live_chat_url_Value", "sender_name": "sender_name_Value", "help_url": "help_url_Value", "company_name": "company_name_Value", "company_address": "company_address_Value" } } } } } } } ``` The `message.providers.postmark.override.config` property can be used to override Courier templates and use Postmark's [Templates API](https://postmarkapp.com/developer/api/templates-api). Be sure to include the `https://api.postmarkapp.com/email/withTemplate` URL in the config object along with the `TemplateId` and `TemplateModel` in the the body. When using Postmark templates - make sure your request uses a `template` parameter instead of `content`. Otherwise the `content` object will take presedence over the Postmark override. The `template_id` must be valid or an error will occur. # Resend Source: https://www.courier.com/docs/external-integrations/email/resend Send email notifications via Courier using Resend by setting up an API key, configuring sender details, and using overrides for advanced customization. ## Setup You will need both a [Courier](https://app.courier.com/signup) and a [Resend](https://resend.com/signup) account. In Resend, navigate to [API Keys](https://resend.com/api-keys). Create an API key with appropriate permissions and copy it. In Courier, navigate to the [Resend Integration](https://app.courier.com/integrations/catalog/resend) page. Paste your API key into the "API Key" field. Add a verified email address to the "From Address" field (e.g., `noreply@yourdomain.com`). Click "Add Integration" then "Save." ## Profile Requirements To deliver a message to a recipient over Resend, Courier must be provided the recipient's email address. This value should be included in the recipient profile as `email`. ```json theme={null} { "message": { "to": { "email": "alice@acme.com" } // ... rest of message definition } } ``` ## Overrides You can use a provider override to customize what Courier sends to Resend's [/emails endpoint](https://resend.com/docs/api-reference/emails/send-email). Overrides are useful when a field is not yet supported by Courier or you want to replace a value that Courier generates. For example, you can use Resend's tagging feature: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "resend": { "override": { "body": { "tags": [{ "name": "environment", "value": "development" }] } } } } } } ``` Everything inside `message.providers.resend.override.body` is merged with Courier's generated request body. You can also override config values: ```json theme={null} { "message": { "providers": { "resend": { "override": { "config": { "apiKey": "", "fromAddress": "alternate-sender@yourdomain.com" } } } } } } ``` ### Attachments To include an attachment, add an `attachments` array in the override. File content must be base64-encoded. ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "resend": { "override": { "attachments": [ { "filename": "billing.pdf", "contentType": "application/pdf", "data": "Q29uZ3JhdHVsYXRpb25zLCB5b3UgY2FuIGJhc2U2NCBkZWNvZGUh" } ] } } } } } ``` # SendGrid Email Integration Source: https://www.courier.com/docs/external-integrations/email/sendgrid Send email via SendGrid through Courier. Configure an API key with Mail Send permissions, set a From Address, and use overrides for any v3 mail send field. ## Setup You will need both a [Courier](https://app.courier.com/signup) and a [SendGrid](https://signup.sendgrid.com/) account. In Courier, navigate to the [**Integrations**](https://app.courier.com/integrations) page and click on the SendGrid Integration to configure it. Create a [SendGrid API key](https://app.sendgrid.com/settings/api_keys) in your SendGrid account with "Mail Send" permissions. Copy the API key and paste it into the "API Key" field in the Courier SendGrid configuration. Make sure your SendGrid API key has the correct permissions. Without proper permissions, you may encounter authentication errors. To receive `delivered` events from SendGrid, your [SendGrid API key permissions](https://sendgrid.com/en-us/blog/introducing-api-key-permissions) need to include `email activity` and `inbound parse` so that `delivered` events can be counted in your message metrics. Add an email address to the "From Address" field (e.g., [noreply@example.com](mailto:noreply@example.com)). Click "Add Integration" and then "Save" to complete the setup. ## Profile Requirements To deliver a message to a recipient over SendGrid, Courier must be provided the recipient's email address. This value should be included in the recipient profile as `email`. ```json icon="code" lines highlight={3} theme={null} { "message": { "to": { "email": "user@example.com" } // ... rest of message definition } } ``` ## Overrides Overrides can be used to change the request body that Courier uses to send an email. Overrides are useful when a field is not yet supported by Courier or you would like to override the value that Courier generates. You can override any of the fields supported by SendGrid's `/mail/send` endpoint ([see all v3 mail send request body fields in SendGrid's API documentation](https://docs.sendgrid.com/api-reference/mail-send/mail-send)). Below is an example of overriding the subject and adding an attachment: ```json icon="code" lines highlight={8-15} focus={1-20} theme={null} { "message": { "template": "appointment-reminder", "to": { "email": "user@example.com" }, "data": { "name": "Nomen Nescio" }, "providers": { "sendgrid": { "override": { "body": { "subject": "Your appointment reminder", "attachments": [ { "content": "eyJmb28iOiJiYXIifQ==", "type": "application/json", "filename": "appointment-details.json" } ] } } } } } } ``` ## Email Activity Tracking For the SendGrid configuration, the toggle, *Enable Email Activity Tracking via Polling*, will allow Courier to use SendGrid's Email Activity API to periodically check on the delivery status of sent emails. The API Key must have Read Access to the Email Activity, and the SendGrid account must have the additional email history add-on enabled. See the documentation for [SendGrid API Keys](https://app.sendgrid.com/settings/api_keys) and [SendGrid Email Activity](https://app.sendgrid.com/settings/billing/addons/email_activity) to ensure the SendGrid account is set up correctly. ## Template Import You can import your SendGrid templates to use with Courier from the [Courier SendGrid configuration page](https://app.courier.com/channels/sendgrid). Before you can successfully import SendGrid Dynamic Templates, you will need to make sure of the following: * SendGrid templates must be saved as [SendGrid Dynamic Templates](https://docs.sendgrid.com/ui/sending-email/how-to-send-an-email-with-dynamic-templates). * Your SendGrid API key must have [full access permissions](https://docs.sendgrid.com/ui/account-and-settings/api-keys) for `Template Engine`. * You will need to provide your SendGrid credentials in the configuration page to retrieve your saved templates from SendGrid. ### Import Process Once your SendGrid API key has the appropriate permissions, you will be able to see your templates ready for import from the template import tool. Templates ready for import will appear as selectable checkboxes that you can choose to import. ## Delivery Tracking By default, after we send a message, Courier will poll SendGrid periodically to find out if the message was delivered successfully or not. To get faster status updates, you can setup a webhook so SendGrid can report delivery status directly to Courier. In Courier, visit the [Courier SendGrid provider configuration screen](https://app.courier.com/channels/sendgrid) (from the Channels menu on the left side). There, you will find a Webhook URL. Copy the webhook URL, then login to SendGrid. On the left side, choose Settings, then Mail Settings. Next, click on the Event Webhooks link. On the next page, click the Create new webhook button. Then fill out the form: * **Friendly Name** - Whatever you like * **Post URL** - Paste the URL you copied earlier from Courier's website. * **Deliverability Data** - Check all 5 boxes. * **Security features** - You can leave these disabled. The HTTPS URL you pasted includes a cryptographic token that Courier can use to verify incoming events belong only to you. Hit the Save button. Now that the webhook is configured, you will no longer need Courier to poll for status updates. Wait about an hour before disabling polling to make sure there is no gap in status reporting of any messages already in-flight. When you are ready, return to the [Courier SendGrid configuration screen](https://app.courier.com/channels/sendgrid). Flip the toggle switch labeled "Enable polling for status updates" into the off position. Then, press the Save button at the bottom of the screen. ## Troubleshooting If you encounter issues while using SendGrid with Courier, the troubleshooting guide below can help you resolve common problems. You can also check the [Courier Logs page](https://app.courier.com/logs) to help debug any provider errors you may encounter. For anything else, you may contact [Courier Support](mailto:support@courier.com). This issue occurs when the request you are trying to perform is not listed under [SendGrid's allowed API key actions](https://docs.sendgrid.com/api-reference/api-key-permissions/api-key-permissions) of the SendGrid API key that you are using. #### Solution You can do one of the following. 1. You can perform actions allowed by the API key you generated. 2. Create a new SendGrid API key that allows the actions you wish to execute. **Creating the new SendGrid API key with desired permissions** When creating the SendGrid API key, you can [select the permissions in SendGrid's API key settings](https://docs.sendgrid.com/ui/account-and-settings/api-keys) that you need in the setup guide. To do so, visit the SendGrid API keys console and select "Create API Key." It displays the figure shown below. As illustrated above, SendGrid allows you to select three permission scopes: * Full Access * Restricted Access * Billing Access. In addition, you can select the access levels that you require for your API Key based on your requirements. Whenever you try to send an email to an address that does not comply with internet email formatting standards, or if the email does not present on the recipient's mail server, you will get an invalid email error. This response might originate from either your server or the receiver's mail server. SendGrid checks the email address format before sending it to ensure that it is legitimate. If the receiver server cannot locate the address, it will return a 550 bounce, indicating that the email address is invalid. Inactive recipients who don't connect with your content have invalid email addresses. In addition, for a variety of reasons, unengaged email addresses may be invalid: * Invalid emails may contain typos or misformatting, preventing them from reaching a legitimate inbox. * An email can become invalid if the user's email address changes, leaving the prior email address empty. The biggest cause of emails being invalid is inactivity or lack of engagement. * When an inbox provider, such as Earthlink, goes out of business or a server goes down for good, all email domains under that provider become "dead." The emails linked to this defunct domain are similarly invalid. #### Solution * Email verification for cleaning or "scrubbing" email lists to increase email deliverability and engagement rates using a variety of digital email verification tools. * Sort your email list by why each address signed up for your newsletter. You can use this method to see if your emails are relevant to your readers. If your email communications are irrelevant or out-of-date, it can affect your total engagement rate by lowering open and click-through rates. * Contacts can be divided into groups based on their level of interaction. Double-check that it is still legitimate if you come across an email that appears to be graymail. If not, add the address to the unsubscribe or opt-out list. * Create a sunset policy. A sunset policy's purpose is to discover disengaged contacts regularly and remove or re-engage them from your email lists. SendGrid credits denote the number of emails that you can send. SendGrid uses one credit per email, and these credits get renewed at the start of every month. Therefore, [you may encounter this issue in SendGrid's SMTP troubleshooting guide](https://docs.sendgrid.com/for-developers/sending-email/smtp-errors-and-troubleshooting) if you exceed the maximum number of emails your SendGrid account can send. #### Solution You will have to wait until your quota gets renewed to resolve this error. If this is not an option for you, you can upgrade your SendGrid plan and get more credits based on your requirements. This issue can occur due to the following reasons: * Username and password are incorrect. Error 535 occurs when the username and password entered in the Email client are incorrect. Using the incorrect mail server can potentially result in authentication difficulties. * The account has been disabled. Accounts might be disabled for a variety of reasons, including past-due payments or spamming difficulties. * SMTP Authentication error can occur if your email client does not have SMTP authentication enabled. #### Solution * Check your username and password, billing plans, and account status. * Check whether you have confirmed your email address. * Configure SMTP Authentication. * Your API Keys should be saved as environment variables. This is a far safer technique, with the additional benefit of only having to change them once rather than searching for them every time they're used. * Set up sender authentication for domains so that SPF and DKIM can be set up. This functionality enables you to use industry-standard email authentication protocols to authenticate your domains with the Twilio SendGrid account. This indicates that the `from` address does not correspond to a verified Sender Identity. This issue prevents an email from being sent until it is resolved. #### Solution Customers are asked to [authenticate their sender identities in SendGrid's sender authentication guide](https://docs.sendgrid.com/glossary/sender-authentication) to sustain the highest attainable sender reputations and to maintain legal sending behavior. The address your receivers will view as the sender of your emails is represented by a sender identity. You can use Domain Authentication or Single Sender Verification to authenticate one or more Sender Identities. # SMTP Email Integration Source: https://www.courier.com/docs/external-integrations/email/smtp Send email via SMTP with Courier's NodeMailer integration. Configure host, port, and credentials once; override transport settings or add attachments. ## Setup Courier's SMTP integration uses [NodeMailer](https://nodemailer.com) under the hood. In Courier, navigate to the [SMTP Integration](https://app.courier.com/integrations/catalog/smtp) page and enter your SMTP host, username, password, and From Address, then click "Save." You can override these settings on a per-message basis using provider overrides. Courier connects to your SMTP server from AWS-hosted infrastructure and does not use fixed outbound IPs. If your server requires IP allowlisting, see the [Allowlist for AWS IP Addresses](/docs/external-integrations/email/intro-to-email#allowlist-for-aws-ip-addresses) section on the Email Providers page. ## Profile Requirements To deliver a message to a recipient over SMTP, Courier must be provided the recipient's email address. This value should be included in the recipient profile as `email`: ```json icon="code" theme={null} { "message": { "to": { "email": "recipient@example.com" } // ... rest of message definition } } ``` ## Overrides You can override both the message content and SMTP transport configuration on a per-message basis using provider overrides in the Send API. ### Message Override You can use a provider override to replace what we send to SMTP using NodeMailer. For example, you can add an attachment to your request: ```json icon="code" highlight={11-17} theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "smtp": { "override": { "body": { "attachments": [ { "filename": "document.pdf", "content": "aGVsbG8gd29ybGQh", "encoding": "base64", "contentType": "application/pdf" } ] } } } } } } ``` Everything inside of `message.providers.smtp.override.body` will be merged with Courier's generated message and passed directly to NodeMailer. You can see all the available options by visiting the [NodeMailer message options documentation](https://nodemailer.com/message). ### Transport Override You may also override the [SMTP transport configuration](https://nodemailer.com/smtp) using values passed to `message.providers.smtp.override.config`. These settings will override your stored provider configuration for this specific message. **Basic Example:** ```json icon="code" highlight={9-15} theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "smtp": { "override": { "config": { "auth": { "user": "username", "pass": "hunter2" }, "host": "smtp.example.com", "secure": true, "port": 465 } } } } } } ``` **STARTTLS (port 587, recommended):** ```json icon="code" highlight={10-17} theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "smtp": { "override": { "config": { "host": "smtp.yourdomain.com", "port": 587, "secure": false, "requireTLS": true, "auth": { "user": "user@yourdomain.com", "pass": "your-password" } } } } } } } ``` **Implicit TLS (port 465):** ```json icon="code" highlight={10-16} theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "smtp": { "override": { "config": { "host": "smtp.yourdomain.com", "port": 465, "secure": true, "auth": { "user": "user@yourdomain.com", "pass": "your-password" } } } } } } } ``` **Common provider settings:** | Provider | Host | Port | secure | requireTLS | | --------------------- | --------------------- | ---- | ------ | ---------- | | Office 365 | `smtp.office365.com` | 587 | false | true | | Gmail (app password) | `smtp.gmail.com` | 587 | false | true | | On-prem Exchange | `mail.yourdomain.com` | 587 | false | true | | Custom (implicit TLS) | varies | 465 | true | - | **Transport options reference:** | Option | Type | Description | | ------------------- | ------- | -------------------------------------------------------------------------------- | | `host` | string | SMTP server hostname | | `port` | number | SMTP port (commonly 25, 465, 587) | | `secure` | boolean | Use SSL (true for port 465) | | `requireTLS` | boolean | Require STARTTLS (true for port 587) | | `auth` | object | Authentication credentials `{ user, pass }` | | `tls` | object | TLS options (see [NodeMailer TLS docs](https://nodemailer.com/smtp#tls-options)) | | `connectionTimeout` | number | Connection timeout in milliseconds | | `socketTimeout` | number | Socket timeout in milliseconds | **Available Options:** See the [NodeMailer SMTP transport documentation](https://nodemailer.com/smtp) for all available `config` override options. ## Security Best Practices * Use app-specific passwords for Office 365 and Gmail instead of regular account passwords. * Create dedicated service accounts for sending rather than using personal accounts. * Store credentials in Courier Studio rather than passing them in every API request. * Always use TLS: set `requireTLS: true` for port 587, `secure: true` for port 465. * For HIPAA or data residency requirements, use a direct connection to your on-premises SMTP server. ## Troubleshooting Courier verifies your SMTP connection before each send. If verification fails, the message is not sent and an error is returned. Connection timeouts (`ETIMEDOUT`) and temporary server unavailability are retried automatically. * Check that firewall rules allow outbound connections to your SMTP server * Verify the SMTP host and port are correct * Ensure your SMTP server is accessible from Courier's infrastructure * Verify username and password are correct * For Office 365, ensure you're using an app-specific password if MFA is enabled * Check if your account has permission to send emails via SMTP * Verify your SMTP server supports the requested encryption method * Check certificate validity if using custom certificates * Ensure `secure` and `requireTLS` settings match your server configuration # SparkPost Email Integration Source: https://www.courier.com/docs/external-integrations/email/sparkpost Send email via SparkPost Transmissions API using Courier. Put the recipient email in their profile and override the request to attach files or add fields. ## Setup You will need a [SparkPost](https://www.sparkpost.com/) account. In SparkPost, create an API key with "Transmissions: Read/Write" permissions. In Courier, navigate to the [SparkPost Integration](https://app.courier.com/integrations/catalog/sparkpost) page, enter your API key and From Address, then click "Save." ## Profile Requirements To deliver a message to a recipient over SparkPost, Courier must be provided the recipient's email address. This value should be included in the recipient profile as `email`. ```json theme={null} { "message": { // Recipient Profile "to": { "email": "alice@acme.com" } // ... rest of message definition } } ``` ## Overrides You can use a provider override to replace what we send to SparkPost's Transmissions API. For example, you can add an attachment to your request: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "sparkpost": { "override": { "body": { "content": { "attachments": [ { "name": "billing.pdf", "type": "application/pdf", "data": "Q29uZ3JhdHVsYXRpb25zLCB5b3UgY2FuIGJhc2U2NCBkZWNvZGUh" } ] } } } } } } } ``` Everything inside of `message.providers.sparkpost.override` will replace what we send to SparkPost's Transmissions API. You can see all the available options by visiting [SparkPost API docs](https://developers.sparkpost.com/api/transmissions/#header-request-body). # Integrations Overview Source: https://www.courier.com/docs/external-integrations/integrations-overview Connect Courier to your email, in-app, SMS, push, and chat providers to enable multichannel delivery. ## Integration Platform Courier integrates with 50+ notification providers across **email**, **in-app**, **SMS**, **push**, and **chat** channels. You can connect the providers your system already uses—like SendGrid, Twilio, Firebase, Slack, and others—and send notifications through them with a single API call. Using Courier allows you to: * Route messages through preferred or region-specific providers * Automatically fail over to backups when a provider is unavailable * Track delivery performance and error rates in real time * Enable full message log observability for engineering and support teams After setup, Courier handles message delivery through your connected providers, managing routing, retries, and rate limits automatically. *** ## Setting Up Your First Provider ### Provider Configuration To connect a provider: 1. **Get API credentials** — Obtain your API key or authentication token from the provider. 2. **Add the provider in Courier** — In your Courier workspace, open the [Integrations Catalog](https://app.courier.com/integrations/catalog) and add a new provider from the available list. 3. **Test the connection** — Verify your credentials and confirm that Courier can successfully send through the provider. 4. **Configure routing** — Decide how Courier uses this provider for message delivery and define channel priorities or fallbacks. For detailed setup steps, see [Channel Settings](/docs/platform/sending/channel-settings). Provider API keys are configured **per environment**. If you set up SendGrid in your Production environment, you must also add the API key in your Test environment to send test messages. A missing provider configuration results in a `MISSING_CONFIGURATION` error in your message logs. *** ### Provider Categories Courier supports providers across five main channel types: * **Email** — [SendGrid](/docs/external-integrations/email/sendgrid), [Mailgun](/docs/external-integrations/email/mailgun), [AWS SES](/docs/external-integrations/email/aws-ses), and more * **In-App** — Courier’s built-in [Inbox](/docs/platform/inbox/inbox-overview) for web and mobile applications * **SMS** — [Twilio](/docs/external-integrations/sms/twilio), [MessageBird](/docs/external-integrations/sms/messagebird), [Plivo](/docs/external-integrations/sms/plivo), and more * **Push** — [Firebase FCM](/docs/external-integrations/push/firebase-fcm), [Airship](/docs/external-integrations/push/airship), [OneSignal](/docs/external-integrations/push/onesignal-push), and more * **Chat** — [Slack](/docs/external-integrations/direct-message/slack), [Microsoft Teams](/docs/external-integrations/direct-message/microsoft-teams), [WhatsApp](/docs/external-integrations/direct-message/whatsapp), and more Each integration page includes detailed setup steps and configuration options. [**Courier Inbox**](/docs/platform/inbox/inbox-overview) provides a ready-to-use, in-app notification center that syncs with other channels automatically. You can customize its appearance, handle read states, and track engagement—all without building a notification UI from scratch. *** ## Monitoring Provider Performance ### Provider Metrics Track provider health and performance directly in Courier’s **Analytics Dashboard**.\ Monitor delivery rates, latency, and error rates to quickly detect issues and optimize routing. For details on monitoring and analytics, see [Analytics Overview](/docs/platform/analytics/analytics). *** ## Integration Features ### Fallback and Routing Courier automatically retries or switches to backup providers when a primary provider fails. You can define custom routing logic to control which providers are used and in what order. Learn more in [Channel Priority](/docs/platform/sending/channel-priority). *** ### Rate Limit Management When a provider returns a rate limit error (HTTP 429), Courier automatically queues the message and retries using exponential backoff: | Attempt | Delay | | ------- | ---------- | | 1 | 5 seconds | | 2 | 10 seconds | | 3 | 20 seconds | | 4 | 40 seconds | | 5 | 60 seconds | | 6 | 2 minutes | | 7 | 4 minutes | | 8 | 8 minutes | | 9 | 12 minutes | | 10+ | 15 minutes | Retries continue for up to **24 hours** (approximately 104 total attempts). If the provider includes a `Retry-After` header, Courier respects that timing instead of the default backoff schedule. After exhausting retries, Courier attempts [failover](/docs/platform/sending/failover) to an alternative provider or channel if one is configured. If no alternatives exist, the message is marked as `UNDELIVERABLE`. For a full overview of retry strategies across message delivery, status tracking, and webhooks, see [Delivery Reliability & Retries](/docs/platform/sending/delivery-pipeline-resilience). *** ## Available Providers Browse the integration categories below for provider-specific setup guides and configuration options. Configure email delivery Configure SMS delivery Configure push delivery Configure chat and direct message delivery Sync user data from Segment and Rudderstack Monitor delivery with Datadog and New Relic # Datadog Observability Integration Source: https://www.courier.com/docs/external-integrations/observability/datadog Forward Courier message, channel, and automation metrics to Datadog. Covers API key setup, the downloadable dashboard JSON, and all courier.* metrics. ## Setup 1. Create a Datadog account if you don't have one. 2. Generate a Datadog [API key](https://app.datadoghq.com/organization-settings/api-keys). 3. Navigate to the [Datadog integration page](https://app.courier.com/integrations/catalog/datadog) in Courier and enter the API key you generated in step 2. 4. Verify that the [Datadog site parameter](https://docs.datadoghq.com/getting_started/site/#access-the-datadog-site) corresponds to the URL of your Datadog instance. The Datadog integration is **forward-only**. Courier begins emitting metrics from the moment you activate the integration; it does not backfill historical data. ## Dashboard Download the Courier Datadog Dashboard using the link below to easily monitor Courier within Datadog. [Download Dashboard JSON](https://github.com/trycourier/shareable/blob/main/courier-datadog-dashboard.json) Datadog Dashboard ## Available Datadog Metrics Courier sends various metrics to Datadog to help you monitor and track the performance of your notifications. Here are the available metrics: DataDog's Metrics Without Limits lets users [configure a blocklist](https://docs.datadoghq.com/metrics/metrics-without-limits/#blocklist-of-tags) of tags to reduce cardinality as well as costs. Please note that you will still be [billed](https://docs.datadoghq.com/account_management/billing/custom_metrics/?tab=countrate) for ingestion, but at a lower rate. ### Account Metrics * `courier.account.created` * `courier.account.deleted` * `courier.account.updated` ### Account User Relationship Metrics * `courier.account.user.added` * `courier.account.user.removed` * `courier.account.user.updated` ### Audience Metrics * `courier.audience.matched` * `courier.audience.unmatched` ### Automation Metrics * `courier.automation.invoked` * `courier.automation.throttled` * `courier.automation.digest.triggered` * `courier.automation.batch.triggered` * `courier.automation.fetch-data-step.errored` ### Channel Metrics * `courier.email.delivered` * `courier.email.error` * `courier.email.sent` * `courier.email.undeliverable` * `courier.sms.delivered` * `courier.sms.error` * `courier.sms.sent` * `courier.sms.undeliverable` * `courier.push.delivered` * `courier.push.error` * `courier.push.sent` * `courier.push.undeliverable` * `courier.direct_message.delivered` * `courier.direct_message.error` * `courier.direct_message.sent` * `courier.direct_message.undeliverable` ### Debug Metric * `courier.debug` ### Inbox Metrics * `courier.inbox.connection_attempt` * `courier.inbox.connection_error` * `courier.inbox.reconnection_attempt` * `courier.inbox.notification_attempt` ### List Metrics * `courier.list.subscribed` * `courier.list.unsubscribed` * `courier.list.subscribe.failed` ### Message Metrics * `courier.message.canceled` * `courier.message.delivered` * `courier.message.filtered` * `courier.message.received` * `courier.message.sent` * `courier.message.throttled` * `courier.message.total.clicked` * `courier.message.total.opened` * `courier.message.undeliverable` * `courier.message.unique.clicked` * `courier.message.unique.opened` * `courier.message.unique.sent` * `courier.message.unmapped` * `courier.message.unroutable` ### User Metrics * `courier.user.created` * `courier.user.deleted` * `courier.user.unsubscribed` * `courier.user.updated` * `courier.user.update.failed` You can use these metrics to create custom dashboards and alerts in Datadog to monitor the health and performance of your Courier notifications. ## Troubleshooting | Symptom | Cause | Fix | | --------------------------------------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | No metrics appearing in Datadog | API key incorrect, or Datadog site parameter mismatch | Verify the API key and [site parameter](https://docs.datadoghq.com/getting_started/site/#access-the-datadog-site) match your Datadog instance | | Metrics start from integration date, not historical | The integration is forward-only | Expected behavior — Courier does not backfill. Enable the integration before you need the data | | Metrics appear in test but not production (or vice versa) | The integration is configured per environment | Install the Datadog integration in each Courier environment (test and production) separately | | `courier.message.*` counts don't match message logs | Metrics are emitted per status transition, not per message | A single message may trigger multiple `message.updated` metrics as it progresses through statuses | # Observability Source: https://www.courier.com/docs/external-integrations/observability/intro-to-observability Learn how to integrate Courier with observability providers and improve your metrics and logs tracking capabilities. This feature is available to [Enterprise Tier](https://www.courier.com/pricing/) customers. Courier provides observability integrations to help you gain insights into your message delivery. You can use these insights to understand how your messages are being delivered and to identify any issues. Courier supports [Datadog](/docs/external-integrations/observability/datadog), [New Relic](/docs/external-integrations/observability/new-relic), and [OpenTelemetry](/docs/external-integrations/observability/open-telemetry) as observability integrations. ## Metrics Courier provides the following metrics: ### Account Metrics * `courier.account.created` * `courier.account.updated` * `courier.account.deleted` ### Account User Relationship Metrics * `courier.account.user.added` * `courier.account.user.removed` ### Automation Metrics * `courier.automation.invoked` * `courier.automation.throttled` * `courier.automation.digest.triggered` * `courier.automation.batch.triggered` * `courier.automation.fetch-data-step.errored` ### Message Delivery * `courier.message.received` * `courier.message.delivered` * `courier.message.filtered` * `courier.message.throttled` * `courier.message.unmapped` * `courier.message.unroutable` * `courier.message.undeliverable` * `courier.message.sent` * `courier.message.unique.sent` * `courier.message.unique.opened` * `courier.message.unique.clicked` * `courier.message.total.opened` * `courier.message.total.clicked` ### User Profile * `courier.user.created` * `courier.user.updated` * `courier.user.deleted` ### Channel Delivery \[channel] can be any of the following: `email`, `sms`, `push`, or `chat`. * `courier.[channel].delivered` * `courier.[channel].error` * `courier.[channel].sent` * `courier.[channel].undeliverable` ### Inbox Connection Metrics * `courier.inbox.connection_attempt` * `courier.inbox.connection_error` * `courier.inbox.notified` * `courier.inbox.reconnection_attempt` ### List Subscription and Unsubscription * `courier.list.subscribed` * `courier.list.unsubscribed` ### Audiences * `courier.audience.matched` * `courier.audience.unmatched` Metrics are pre-aggregated by a tumbling window of 10 seconds which are then sent in real-time to your observability integration when a message is `delivered`, `sent`, or `undeliverable` from Courier. ## Logs Courier also provides logs that correspond to the metrics. These logs are also sent to your observability integration. Logs are sent in near real-time. All the metrics and logs are segmented by your Courier `environment`, which could be `production` or `test`. You can use this to filter the metrics and logs by environment. We are working on adding more metrics and logs. If you have any suggestions, please [reach out to support](mailto:support@courier.com). # New Relic Observability Integration Source: https://www.courier.com/docs/external-integrations/observability/new-relic Send Courier delivery metrics and logs to New Relic. Add your license key, customize ingestion endpoints, and import the Courier dashboard JSON. ## Setup * Create a New Relic account if you don't have one. * Retrieve your [License key](https://one.newrelic.com/api-keys) for your account. * Login to Courier and navigate to the Channels page. * Click on New Relic channel and enter the License key from step 2 in the API Key field. ## New Relic Metrics Endpoint You can optionally specify a [Metrics Endpoint](https://docs.newrelic.com/docs/data-apis/ingest-apis/metric-api/report-metrics-metric-api/#api-endpoint) for your New Relic integration. If no endpoint is provided, the United States (US) endpoint is used. ## New Relic Logs Endpoint You can optionally specify a [Logs Endpoint](https://docs.newrelic.com/docs/logs/log-api/introduction-log-api/#endpoint) for your New Relic integration. If no endpoint is provided, the United States (US) endpoint is used. ## Dashboard Download the Courier New Relic Dashboard using the link below to easily monitor Courier within New Relic. [Download Dashboard JSON](https://github.com/trycourier/shareable/blob/main/courier-new-relic-dashboard.json) ## Dashboard Preview New Relic Dashboard # OpenTelemetry Source: https://www.courier.com/docs/external-integrations/observability/open-telemetry Export Courier logs and metrics to any OpenTelemetry-compatible platform using Basic or Bearer Token auth and a custom OTLP endpoint, no collector needed. Export Courier logs and metrics to any OpenTelemetry-compatible (OTel) observability platform by configuring the OpenTelemetry integration in the Courier dashboard. Courier supports both **Basic auth** and **Bearer Token** authorization, so you can connect directly to most providers without an OTel Collector or proxy. ## Setup In Courier, go to **Channels** (or **Integrations**) and open the **OpenTelemetry** integration. Turn the **Enabled** toggle on. Set **Authorization type** to match your provider: * **Basic** — enter your username and password. Used by providers like Grafana Cloud. * **Bearer Token** — enter your API key or token. Used by providers that accept bearer tokens. Enter your provider's OTLP endpoint URL in **OTLP endpoint**. For example: * Grafana Cloud: `https://otlp-gateway-.grafana.net/otlp` * Self-hosted collector: `https://otel-collector.example.com:4318` If your endpoint uses custom paths, fill in **Metrics path** and **Logs path**. Common defaults are `/v1/metrics` and `/v1/logs`. Leave blank to use the endpoint defaults. Click **Complete**. Courier will begin exporting logs and metrics to your platform. ## Configuration reference | Field | Required | Description | | ---------------------- | ----------------- | --------------------------------------------------------- | | **Enabled** | — | Toggle the integration on or off. | | **Authorization type** | Yes | **Basic** or **Bearer Token**. | | **Username** | When using Basic | Username for Basic auth (e.g. Grafana Cloud Instance ID). | | **Password** | When using Basic | Password for Basic auth (e.g. Grafana Cloud API token). | | **Bearer token** | When using Bearer | API key or token for Bearer auth. | | **OTLP endpoint** | Yes | Your provider's OTLP endpoint URL. | | **Metrics path** | No | Custom path for metrics (e.g. `/v1/metrics`). | | **Logs path** | No | Custom path for logs (e.g. `/v1/logs`). | Use HTTPS and dedicated credentials for the Courier integration so you can rotate or revoke them without affecting other services. ## What gets exported Courier sends **logs** and **metrics** over the [OTLP](https://opentelemetry.io/docs/specs/otlp/) protocol. For the full list of available metrics (message delivery, automations, account events, etc.), see [Observability](/docs/external-integrations/observability/intro-to-observability#metrics). *** ## Grafana Cloud OTel setup guide Courier connects directly to Grafana Cloud using Basic auth. No OTel Collector or proxy is required. ### Step 1: Get your credentials 1. Sign in to the [Grafana Cloud Portal](https://grafana.com/auth/sign-in/). 2. Select your stack and click **Configure** on the **OpenTelemetry** tile. 3. Note your **Instance ID** (a numeric value) and your **OTLP endpoint URL** (e.g. `https://otlp-gateway-prod-us-east-0.grafana.net/otlp`). 4. Generate a **Cloud API token** with `metrics:write` and `logs:write` scopes. ### Step 2: Configure Courier In the Courier dashboard, open the OpenTelemetry integration and enter: | Field | Value | | ---------------------- | ------------------------------------------------ | | **Enabled** | On | | **Authorization type** | Basic | | **Username** | Your Instance ID | | **Password** | Your Cloud API token | | **OTLP endpoint** | `https://otlp-gateway-.grafana.net/otlp` | | **Metrics path** | *(leave blank)* | | **Logs path** | *(leave blank)* | Click **Complete** to save. ### Step 3: Verify 1. In Grafana Cloud, go to **Explore**. 2. Select your **Mimir** data source and filter for `{service_name="courier"}`. 3. Switch to **Loki** to confirm logs are arriving. Data should appear within a few minutes of enabling the integration. # AWS SNS Source: https://www.courier.com/docs/external-integrations/other/aws-sns Learn how to send push, SMS, and email notifications using Courier with AWS SNS, including setup, profile requirements, and support for override credentials. ## Setup The AWS SNS provider is listed as a Push integration but can handle push, SMS, and email notifications. To add it to your workflow in the designer, follow one of these paths: 1. **Add AWS SNS as a channel provider:** Click "Add Channel" and select the Push channel. A Push option will be added to the left menu under "Channels". Hover over this and click on the Settings icon that will appear. In the pop-up, navigate to "+ Add Integration" and search for AWS SNS. Push Channel Push Channel Settings 2. **Add AWS SNS as a provider:** Click "Add Channel" and use the search bar to find "AWS SNS" AWS SNS Integration ## Profile Requirements ### Push To deliver a message to a mobile device over SNS, Courier must be provided either the Topic ARN, that the device is subscribed to, or the Target ARN that the device is subscribed to. If using a Target ARN, then this value should be included in the recipient profile as `target_arn`. ```json theme={null} { "message": { "to": { "target_arn": "your:target:arn" } } } ``` If no `target_arn` is provided in the recipient profile then the Topic ARN will be used from the Courier configuration. Either the Target ARN or the Topic ARN should be provided, not both. ### SMS To deliver a message to a recipient over AWS SNS, Courier must be provided the recipient's phone number. This value should be included in the recipient profile as `phone_number`. ```json theme={null} { "message": { "to": { "phone_number": "+12345678901" } } } ``` ## Overrides You can override the Access Key ID, Secret Access Key, and region via the `config` override. See all [SNS publish properties](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SNS.html#publish-property). ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": {}, "providers": { "aws-sns": { "override": { "config": { "accessKeyId": "RUNTIME_ACCESS_KEY_ID", "secretAccessKey": "RUNTIME_SECRET_ACCESS_KEY", "region": "eu-west-1" } } } } } } ``` # Custom Webhook Push Provider Source: https://www.courier.com/docs/external-integrations/other/custom-provider Route push notifications to your own webhook endpoint. Configure a URL and auth method in Courier, design blocks in Studio, and receive a push payload. ## Setup To install the Custom Provider, navigate to [Custom Provider integration](https://app.courier.com/integrations/catalog/custom). Input your webhook HTTP address and choose an authentication model. Custom Provider After installing the Custom Provider, you can add it to any Push Channel. After adding a "Push Channel", open up the Channel Settings Modal. Custom Provider Settings You will see "Custom" in the list of "Installed Providers". Installed Custom Provider You can now add a Title and blocks to your designer. They will be sent as both "plain text" and an array of "blocks" to the configured webhook. Custom Provider Designer Upon sending the message, your webhook will receive a payload that looks like this: ```js theme={null} interface TextBlock { type: "text"; text: string; } interface ActionBlock { type: "action"; url: string; text: string; } interface PushMessage { type: "push"; data: { messageId: string; content: { title: string; body: string; blocks: Array } } } ``` ```json theme={null} { "type": "push", "data": { "messageId": "1-6140e057-2749378a31c6026f3dab823f", "content": { "blocks": [ { "text": "My Body", "type": "text" }, { "text": "Click Here", "url": "https://example.execute-api.us-east-1.amazonaws.com/dev/r/TRACKING_ID", "type": "action" } ], "body": "My Body\nClick Here: https://example.execute-api.us-east-1.amazonaws.com/dev/r/TRACKING_ID", "title": "My Title" } } } ``` ## Overrides You can use an override to replace what Courier sends to your custom provider. The `body`, `headers`, `method`, and `url` fields are all overridable. ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "custom": { "override": { "body": {}, "headers": {}, "method": "POST", "url": "https://your-endpoint.example.com/webhook" } } } } } ``` # Drift Source: https://www.courier.com/docs/external-integrations/other/drift Use Courier to trigger Drift conversations by providing a recipient email and optionally overriding Drift's API request body and access token configuration. ## Setup Install the Drift integration from the [Courier integrations catalog](https://app.courier.com/integrations/catalog/drift). You will need your Drift access token. Drift was acquired by Salesloft. The legacy Drift API documentation at `devdocs.drift.com` may no longer be available. Refer to [Salesloft's documentation](https://developers.salesloft.com/) for current API details. ## Profile Requirements To start a conversation with a recipient over Drift, you must provide the recipient's email as `email` in the profile. ```json theme={null} { "message": { "to": { "email": "alice@acme.com" } } } ``` ## Overrides You can override the request body, access token, URL, and headers. Below is an example of overriding the body and access token: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "alice@acme.com" }, "providers": { "drift": { "override": { "body": { "message": { "body": "Custom message body" } }, "config": { "accessToken": "RUNTIME_ACCESS_TOKEN" } } } } } } ``` # Intercom Source: https://www.courier.com/docs/external-integrations/other/intercom Send in-app messages via Intercom by including the user's Intercom ID in their profile, with support for message and sender overrides. ## Setup Install the Intercom integration from the [Courier integrations catalog](https://app.courier.com/integrations/catalog/intercom). You will need your Intercom access token and an admin/bot user ID to send messages from. ## Profile Requirements To deliver an in-app push message to a recipient over Intercom, you must provide the recipient's Intercom User ID. Courier assumes the user is of type `user`. ```json theme={null} { "message": { "to": { "intercom": { "from": "012345", "to": { "id": "INTERCOM_USER_ID" } } } } } ``` ## Overrides You can use the provider override to replace what Courier sends as an Intercom Message. Supported overrides include the message body and the From User ID. Here's an example where both are overridden: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "intercom": { "id": "INTERCOM_USER_ID" } }, "providers": { "intercom": { "override": { "body": { "body": "I overrode this message", "from": { "id": "012345" } } } } } } } ``` Other supported overrides are documented in the [Intercom API reference](https://developers.intercom.com/intercom-api-reference/reference#admin-initiated-conversation). # Opsgenie Alert Integration Source: https://www.courier.com/docs/external-integrations/other/ops-genie Send Courier alerts to Opsgenie by adding the channel to a template. Pass dynamic data in the payload and override the API key, message, or region URL. ## Setup Install the Opsgenie integration from the [Courier integrations catalog](https://app.courier.com/integrations/catalog/opsgenie). To configure the Opsgenie provider, you must set a value in the `Message` field in the channel configuration. You can use a static message or enhance it with the `data` property in the API call payload. OpsGenie Channel Configuration ## Profile Requirements Opsgenie does not require any profile data. If you include the Opsgenie channel in your notification template, Courier will route the notification to Opsgenie. ```json theme={null} { "message": { "to": {}, "template": "TEMPLATE_ID" } } ``` To add dynamic content to your message, include the `data` property in the API call payload: ```json theme={null} { "message": { "data": { "metadata": { "greeting": "Hey... DO NOT PANIC..." } }, "providers": { "opsgenie": { "override": { "config": { "apiKey": "YOUR_OPSGENIE_API_KEY" } } } }, "template": "TEMPLATE_ID", "to": {} } } ``` After processing the request, the notification will include the value from the `data` property. OpsGenie Resulting Notification ## Overrides You can use overrides to change the configuration or request body that Courier sends to Opsgenie. For example, you can change the API key or the message body. If you are using Opsgenie in the Europe region, use the URL `https://api.eu.opsgenie.com/v2` and the API key associated with your EU instance. ```json theme={null} { "message": { "template": "TEMPLATE_ID", "to": {}, "providers": { "opsgenie": { "override": { "config": { "apiKey": "YOUR_OPSGENIE_API_KEY", "url": "https://api.eu.opsgenie.com/v2" }, "headers": {}, "body": { "message": "YOUR MESSAGE" } } } } } } ``` # PagerDuty Alert Integration Source: https://www.courier.com/docs/external-integrations/other/pagerduty Trigger PagerDuty incidents from Courier via Events API v2. Configure routing key, severity, and source; override per-user routing keys and payload fields. ## Setup ### Prerequisites * Pagerduty [account](https://www.pagerduty.com/sign-up/) with elevated privileges * Pagerduty Service created and configured with Events API v2 integration ### Configure PagerDuty Integration 1. In your PagerDuty account, navigate to the desired Service and access its "Integrations" settings. 2. Add a new "Events API v2" integration and configure it according to your requirements. 3. Copy the Integration Key provided by PagerDuty for the newly created integration. 4. Navigate to the [PagerDuty integration setup page](https://app.courier.com/channels/pagerduty) in your Courier account and provide the necessary information, including the Integration Key. | | | | ---------------- | --------------------------------------------------------------------------------------------------- | | **Routing Key** | Paste the Integration Key obtained from PagerDuty. | | **Event Action** | Specify the event action (e.g., trigger) for the notifications. | | **Source** | Enter the host name or fully qualified domain name (FQDN) of the source sending the notifications. | | **Severity** | Choose the appropriate severity level (e.g., info, warning, error, critical) for the notifications. | ## Overrides ### Routing Key You can override the routing key by specifying a `routing_key` in the user profile using `profile.pagerduty.routing_key`. ```json theme={null} { "profile": { "pagerduty": { "routing_key": "PROFILE_SPECIFIC_ROUTING_KEY" } } } ``` If a `routing_key` is specified in `profile.pagerduty`, it will take precedence over the routing key from the main configuration. ### Payload You can use the provider override the payload sent to PagerDuty's Events API. Supported overrides include the Severity and Source. ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "user_id": "1234567890" }, "providers": { "pagerduty": { "override": { "body": { "payload": { "severity": "error", "source": "a different source" } } } } } } } ``` For more information on supported payload overrides, refer to the [PagerDuty Events API v2 documentation](https://v2.developer.pagerduty.com/docs/send-an-event-events-api-v2). # Splunk On Call Alerting Integration Source: https://www.courier.com/docs/external-integrations/other/splunk-on-call Route incident alerts to Splunk On Call (formerly VictorOps) via Courier. Set a target type and slug in the recipient profile and an alert summary. ## Setup Install the Splunk On Call integration from the [Courier integrations catalog](https://app.courier.com/integrations/catalog/splunk-on-call). You will need your Splunk On Call API key and API ID. Splunk On Call was formerly known as VictorOps. The API documentation at `portal.victorops.com` may redirect to Splunk's current documentation. ## Profile Requirements To deliver a message to a recipient over Splunk On Call, you must provide a target object containing the recipient's type and slug as `splunk-on-call` in the profile. ```json theme={null} { "message": { "to": { "splunk-on-call": { "target": { "type": "User", "slug": "userSlug" } } } } } ``` ## Template Requirements To deliver a message to a recipient over Splunk On Call, you must provide a summary in the notification's integration settings. ## Overrides You can override the API key, API ID, username, URL, and request body. Below is an example of overriding the API key and username via the `config` override: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "splunk-on-call": { "target": { "type": "User", "slug": "emustermann" } } }, "providers": { "splunk-on-call": { "override": { "config": { "apiKey": "RUNTIME_API_KEY", "userName": "RUNTIME_USERNAME" } } } } } } ``` # Webhook Integration Source: https://www.courier.com/docs/external-integrations/other/webhook-integration Deliver HTTP requests via Courier's Webhook provider to a static or dynamic URL. Set method, headers, basic or bearer auth, and control profile data sent. ## Setup To configure a static webhook destination, specify the Webhook URL and Authorization type in the [webhook integration setup page](https://app.courier.com/integrations/webhook). ## Profile Requirements To deliver an HTTP request, Courier must be provided with a destination. ### Dynamic Destination If you need to specify the webhook destination on a per-recipient basis, choose "Dynamic Destination" and pass the information in the recipient profile as `webhook`. ```json theme={null} { "message": { "to": { "webhook": { "url": "https://www.example.com", "method": "POST", "headers": { "Content-Type": "application/json" }, "authentication": { "mode": "basic", "username": "AzureDiamond", "password": "hunter2" }, "profile": "expanded" } } } } ``` #### Authentication The webhooks provider supports basic and bearer authentication. Set `authentication.mode` to `basic` or `bearer` and provide the credentials. Authentication defaults to `none` if not provided. ```json theme={null} { "mode": "basic", "username": "AzureDiamond", "password": "hunter2" } ``` ```json theme={null} { "mode": "bearer", "token": "ABCDEFG123456" } ``` #### Expanded Profile You can control what profile information is included in the request payload by setting `profile` to either `limited` or `expanded`. The default is `limited`, which only includes profile data provided when the send API was called. Setting `expanded` includes profile data merged from the profile database. ## Request Payload Based on how the profile is configured, the webhook provider sends the following payload using what was passed into the send method. ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "email": "stan.pines@mysteryshack.com", "phone_number": "+12025550165" }, "data": { "name": "Stan Pines", "location": "Gravity Falls, OR" } } } ``` ## Overrides You can use a provider override to replace what Courier sends to the destination. The `url`, `method`, `headers`, and `body` fields are all overridable. ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "data": { "hello": "world" }, "providers": { "webhook": { "override": { "url": "https://www.example.com", "method": "PUT", "headers": { "X-Custom-Header": "Hello from Courier" }, "body": { "key": "value" } } } } } } ``` # Airship Push Notification Integration Source: https://www.courier.com/docs/external-integrations/push/airship Send push notifications via Airship through Courier. Provide audience and device_types in the profile, use body and header overrides, and track delivery. ## Setup You will need an [Airship](https://www.airship.com/) account with a project configured. In Courier, navigate to the [Airship Integration](https://app.courier.com/integrations/catalog/airship) page, enter your auth token and base URL, then click "Save." ## Profile Requirements To deliver a push notification to a recipient over Airship, Courier must be provided the recipient's Airship audience and device types. These values should be included in the recipient profile as `airship`. ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "airship": { "audience": { "named_user": "user-123" }, "device_types": ["ios"] } }, "data": { "username": "Steph Courier" } } } ``` ## Overrides You can use a provider override to replace what Courier sends to Airship's API. For example, you can use basic auth with your send request: ```json theme={null} { "message": { "template": "NOTIFICATION_TEMPLATE_ID", "to": { "airship": { "audience": { "named_user": "user-123" }, "device_types": ["ios"] } }, "data": { "dataForPushMessage": true }, "providers": { "airship": { "override": { "body": {}, "headers": { "Authorization": "Basic " } } } } } } ``` Everything inside of `message.providers.airship.override` will replace what Courier sends to Airship's API. You can see all the available options by visiting [Airship's API docs](https://docs.airship.com/api/ua/). ## Tracking Events Courier will include tracking URL information in the `global_attributes` data bag. See [push notification tracking](/docs/external-integrations/push/intro-to-push#tracking) for details. # Apple Push Notifications Service (APNS) Source: https://www.courier.com/docs/external-integrations/push/apple-push-notification Integrate APNS with Courier for iOS push notifications. Use the Mobile SDK for token management or supply device tokens via apn.token in the profile.