Quick start
In Web Admin, create a third-party application for the target company. Copy its App ID and App Secret, and configure an optional Callback URL if final results are needed.
Engine creates notification-center entries for users selected by the optional audience (the whole eligible company by default) and attempts system Push only for eligible registered devices.
Endpoint and authentication
API environments:
- Test: https://aim-api-test.proton-system.com
- Production: https://aim-api.proton-system.com
Every server-to-server JSON request must include Authorization: Basic Base64(KEY:SECRET) using HTTP Basic Authentication, where KEY is the App ID and SECRET is the App Secret:
Endpoint: POST /openapi/v1/notifications.
curl --request POST 'https://<your-engine-host>/openapi/v1/notifications' \
--header 'Authorization: Basic <Base64(AppID:AppSecret)>' \
--header 'Content-Type: application/json' \
--data '{"requestId":"order-20260819-001","title":"Order completed","body":"Your order has been completed.","imageUrls":["https://cdn.example.com/order/001-1.webp","https://cdn.example.com/order/001-2.webp"],"displayFields":[{"label":"Store name","value":"Little Han Noodles (Wangjing)"},{"value":"ORD-10086"}],"tags":["Completed","Main store"]}'Request contract
Field summary
| Field | Required | Type | Meaning |
|---|---|---|---|
requestId | Yes | string | Caller-generated unique request identifier and idempotency key. Trimmed and must not be empty. |
title | Yes | string | Notification title. Trimmed and must not be empty. |
body | Yes | string | Notification body. Trimmed and must not be empty. |
imageUrls | No | string[] | Image URLs shown in notification detail, in order. |
displayFields | No | object[] | Additional text fields shown in notification detail, in order. |
tags | No | string[] | Plain-text tags shown only in notification detail, in order. |
audience | No | object | Narrows delivery to users or company roles; omission means all eligible company users. |
Images: imageUrls
- Omit it, send
null, or send[]for no images. Otherwise send 1-9 URLs in display order. - Every URL is trimmed, must be unique, and must be an absolute
HTTPSURL without a username or password. - Each URL may contain at most 2048 Unicode characters. One invalid URL rejects the entire request.
- Engine stores the URLs but never fetches, probes, proxies, or caches them.
Detail fields: displayFields
| Subfield | Required | Limit | Rendering |
|---|---|---|---|
label | No | Trimmed, non-empty, at most 32 Unicode characters | Shown as the field name. |
value | Yes | Trimmed, non-empty, at most 256 Unicode characters | Shown as the field value. |
{
"displayFields": [
{ "label": "Store name", "value": "Little Han Noodles (Wangjing)" },
{ "value": "ORD-10086" }
]
}- Omit it, send
null, or send[]for no extra fields. At most 10 objects are allowed. - A row without
label, or withlabel: null, rendersvalueas one full-width line. - Content is always plain text. HTML, Markdown, links, nested data, field types, and style instructions are unsupported.
Detail tags: tags
- Omit it, send
null, or send[]for no tags. Otherwise send at most 8 strings in display order. - Each tag is trimmed and must contain 1-20 Unicode characters. Empty values, non-string values, overlong values, and duplicates after trimming reject the entire request with
INVALID_REQUEST. - Tags are non-interactive plain text shown only in notification detail. Their order is preserved.
Recipients: audience
type | Selector | ID meaning |
|---|---|---|
USERS | userIds | Company Member ID |
ROLES | roles | CompanyRole ID; every eligible member matching any role receives the notification. |
Copy the Company Member ID from the Web Admin company member list. The member must belong to the company bound to the third-party application.
{
"audience": {
"type": "USERS",
"userIds": ["companyMemberId"]
}
}{
"audience": {
"type": "ROLES",
"roles": ["roleId"]
}
}- Omit
audienceor sendnullto target every eligible active user in the bound company. - The matching list must contain 1-100 raw strings. IDs are trimmed; blanks are ignored; values are deduplicated and sorted as a set.
- Nonexistent, inactive, deleted, or cross-company IDs are ignored while all remaining valid targets receive the notification.
- A valid selector with no valid targets is accepted and completes with
recipientCount=0; it never falls back to company-wide delivery. - The non-matching selector may be omitted or
null. Other mismatches, unknown types or fields, missing or empty matching lists, non-string entries, and more than 100 entries returnINVALID_REQUEST.
Limits and idempotency
- The complete body is limited to 256 KiB.
requestId,title, andbodyallow at most 128, 100, and 1000 Unicode characters respectively. - Each App ID is limited to 10 requests per minute and 100 per hour.
- Retrying the same
requestIdwith equivalent normalized content returns the stored result. Image, display-field, and tags order matters; audience order and duplicates do not. - Reusing
requestIdwith different content returnsIDEMPOTENCY_CONFLICT. A completed request can returnSUCCESS,PARTIAL_SUCCESS, orFAILEDinstead ofACCEPTED.
Acceptance response
A valid request returns HTTP 202:
{
"requestId": "order-20260819-001",
"notificationId": "00000000-0000-0000-0000-000000000001",
"status": "ACCEPTED"
}ACCEPTED means Engine stored the request for dispatch. It does not prove that APNs/FCM or a device delivered the notification.
Callback results
If a Callback URL is configured, Engine sends one terminal result:
{
"requestId": "order-20260819-001",
"status": "PARTIAL_SUCCESS",
"recipientCount": 8,
"pushAcceptedCount": 1,
"pushFailedCount": 1
}recipientCount is the number of notification-center recipients. pushAcceptedCount and pushFailedCount are Push Outbox batch counts, not user counts, device counts, or proof of system-banner delivery.
Callback test
Each admin Callback test sends a distinct probe:
{
"requestId": "callback-test-<unique-id>",
"status": "SUCCESS",
"recipientCount": 1,
"pushAcceptedCount": 1,
"pushFailedCount": 0,
"test": true
}When test is true, validate the shape and return 2xx, but do not match the probe to a submitted notification request or update business delivery state. Its counts are fixed probe values, not real recipients or Push Outbox batches.
Terminal status is SUCCESS, PARTIAL_SUCCESS, or FAILED. Return any 2xx response to acknowledge it.
Otherwise Engine retries after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, and 24 hours. Callbacks currently have no HMAC signature: require HTTPS, match the requestId of non-test results to your own request, restrict exposure where possible, and process callbacks idempotently.
Error reference
| HTTP | Code | Meaning |
|---|---|---|
| 400 | INVALID_REQUEST | Content type, JSON shape, size, or field validation failed. |
| 401 | INVALID_APP_CREDENTIALS | App ID or App Secret is invalid. |
| 403 | APPLICATION_INACTIVE | The application or bound company is inactive. |
| 409 | IDEMPOTENCY_CONFLICT | The requestId was reused with different content. |
| 429 | RATE_LIMITED | The per-application rate limit was exceeded. |
| 503 | NOTIFICATION_UNAVAILABLE | Notification processing is temporarily unavailable. |
Security and delivery scope
- App ID and App Secret are server-to-server credentials. Never embed them in a browser, mobile app, public repository, URL, or client-side storage. Rotate the App Secret if it is exposed.
- Private target IDs are never written to application logs. The private audience selector and target IDs are also excluded from GraphQL, Push, Callback, realtime, and navigation payloads.
- Without
audience, every eligible active user in the bound company receives a notification-center entry. With it, only valid resolved targets do. Those recipients are included inrecipientCount. - System Push is attempted only for devices with an active
PushInstallationthat permits theBUSINESScategory. It still does not guarantee a system banner on every device. - The complete
imageUrlslist is available only in authorized detail. Engine may send the first URL inimageUrlsto APNs/FCM for a native system banner.displayFieldsandtagsare not included in Push or Callback and are non-interactive plain text; never include secrets, access tokens, payment data, or other sensitive values. - The
titleandbodymust remain complete and understandable without the images. Image download or rendering failure does not change the accepted, Callback, or Push status. - The mobile client accepts at most 5 MiB per image and gives the complete download operation 10 seconds. It does not follow 3xx redirects, so every URL must return the image directly.
- The mobile client downloads images from the public CDN. Use no-auth URLs that need no cookies, keep them available for at least 90 days, and account for the CDN receiving the user's IP, request time, and User-Agent.
- Because URL content can change, use immutable object URLs when stable historical display matters.