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:
- Register a separate application for each environment.
- Use account mode
requiredfor a multi-tenant integration. - Create a backend credential with the scopes your workload needs.
- Synchronize the customer accounts.
- Configure account providers for any external channels.
- Publish templates for the exact channel and locale you will send, or explicitly permit raw content.
- 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:
- Authenticate your application's session.
- Resolve the active organization and check membership.
- Derive the stable user ID from the session.
- Call Notify
POST /v1/user-tokensusing those trusted IDs and a key withusers:token_issue. - 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/inboxandGET /v1/user/inbox/unread-count. - Mark messages read through the user inbox endpoints.
- Open
GET /v1/user/realtime/eventsusing 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
| Check | Expected behavior |
|---|---|
| Same request retried with the same idempotency key | Original notification returned |
| Same key reused with a different payload | HTTP 409 |
Sender key lacks notifications:send | HTTP 403 |
Raw message lacks notifications:send_raw | Request rejected by content validation |
| Organization account missing or inactive | Request rejected |
| Account's external provider missing | Delivery reports provider_not_configured |
| User token issued for one organization | Inbox remains bound to that organization and recipient |
| User token expired | User API rejects it; refresh through your backend |
| Queue processing temporarily delayed | Request can be accepted; delivery status remains asynchronous |
Record request IDs and notification IDs for support. Test against dedicated staging recipients before using production.