Skip to main content

Implementation guide

Integrating Notify involves configuration in Console, a backend adapter, and optional frontend inbox/push components. The same steps apply to any application.

1. Configure Notify​

In the tenant workspace:

  1. Register a separate application for each environment.
  2. Use account mode required for a multi-tenant integration.
  3. Create a backend credential with the scopes your workload needs.
  4. Synchronize the customer accounts.
  5. Configure account providers for any external channels.
  6. Publish templates for the exact channel and locale you will send, or explicitly permit raw content.
  7. Create routing rules if you want Notify to choose channels.

You can begin with in_app using the Quickstart, then add external delivery.

2. Add backend configuration​

NOTIFY_URL=https://beta.api.notify.callistech.co.id
NOTIFY_API_KEY=YOUR_BACKEND_SECRET

Inject these values through your deployment's secret/configuration mechanism. Keep a separate configuration for production.

3. Synchronize organization identity​

Implement a backend service calling PUT /v1/accounts/{external_key} with your stable organization ID. Run it on organization creation and relevant updates. A reconciliation job can repair missed synchronization events.

Persist your mapping convention in code: the ID used here must also become account_key in send and user-token requests.

4. Send after the business transaction commits​

When a business event succeeds, enqueue notification work from your backend. Use a stable event identifier to form the idempotency key.

// Server-side JavaScript. The API key must never reach browser code.
export async function sendPaymentNotification({organizationId, userId, invoiceId}) {
const response = await fetch(process.env.NOTIFY_URL + '/v1/notifications', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.NOTIFY_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': 'invoice-paid-' + invoiceId + '-v1',
},
body: JSON.stringify({
account_key: organizationId,
event_key: 'billing.invoice.paid',
recipient: {external_user_id: userId},
channels: ['in_app'],
template_key: 'invoice.paid',
locale: 'en-US',
variables: {invoice_number: invoiceId},
}),
signal: AbortSignal.timeout(10000),
});
const result = await response.json();
if (!response.ok) {
throw new Error('Notify HTTP ' + response.status + ': ' + JSON.stringify(result));
}
return result;
}

This example assumes a published invoice.paid template for in_app and en-US. Use the raw-content quickstart first if you have not configured a template.

Save the returned notification ID alongside the business event. Retry transient network/server failures with the same payload and idempotency key using a bounded job retry policy. Treat validation and permission failures as configuration or input errors. A 409 needs investigation; changing the key can create a duplicate message.

For reliable coupling to your business database, use your application's transactional outbox so a process crash after commit does not silently drop notification work.

5. Add an authenticated token endpoint to your backend​

Create your own route, such as POST /api/notification-access. This is an example application route, not a Notify endpoint.

The route must:

  1. Authenticate your application's session.
  2. Resolve the active organization and check membership.
  3. Derive the stable user ID from the session.
  4. Call Notify POST /v1/user-tokens using those trusted IDs and a key with users:token_issue.
  5. Return the short-lived token and expiry to the frontend.

Do not blindly forward account/user IDs supplied by the browser.

6. Add frontend features​

Use the returned token to:

  • Fetch GET /v1/user/inbox and GET /v1/user/inbox/unread-count.
  • Mark messages read through the user inbox endpoints.
  • Open GET /v1/user/realtime/events using an SSE client that sends Authorization.
  • Register Web Push if the product needs browser notifications.

Configure the frontend origin in Notify's CORS allowlist. Refresh tokens through your backend and reset account-specific state when the user switches organizations.

7. Verify the integration​

CheckExpected behavior
Same request retried with the same idempotency keyOriginal notification returned
Same key reused with a different payloadHTTP 409
Sender key lacks notifications:sendHTTP 403
Raw message lacks notifications:send_rawRequest rejected by content validation
Organization account missing or inactiveRequest rejected
Account's external provider missingDelivery reports provider_not_configured
User token issued for one organizationInbox remains bound to that organization and recipient
User token expiredUser API rejects it; refresh through your backend
Queue processing temporarily delayedRequest can be accepted; delivery status remains asynchronous

Record request IDs and notification IDs for support. Test against dedicated staging recipients before using production.