Skip to main content

Register Device

Register a device (or browser) against a contact so it can receive push notifications, or refresh one that already exists.

Use this when you are migrating existing device tokens from another provider, or when your app cannot embed a client SDK. Apps that embed an SDK do not call this — the SDK registers automatically.

Endpoint

POST /push/devices

Returns: 200 OK

How it behaves

This is an upsert on (organization, installation_id). It is safe to call on every app launch — a repeat call refreshes the device token and attributes rather than creating a duplicate.

The contact must already exist. Unlike Send Event, registering a device never creates a contact.

Registration is not consent, and opt-out is durable

A device revoked by Unregister Device — or by the user opting out in your app — stays revoked. Re-registering does not resurrect it; the response simply reports opted_out: true.

This is deliberate: re-registration happens constantly — on every app launch — and a revocation that any of those calls could undo would silently override the user's choice. For what happens across an app reinstall, see Unregister Device.

Headers

HeaderValueRequired
AuthorizationBearer <your-api-key>Yes
Content-Typeapplication/jsonYes

Body Parameters

Identifying the contact

ParameterTypeRequiredDescription
contact_idstringConditionalArsel contact UUID. Mutually exclusive with email/phone_number.
emailstringConditionalContact email. Matched case-insensitively.
phone_numberstringConditionalContact phone in E.164 format.

Identifying the device

ParameterTypeRequiredDescription
installation_idstringYesYour stable identifier for this app installation. This, not the device token, is the natural key — see below.
platformstringYesandroid, ios, or web.
apns_environmentstringNoproduction or sandbox. iOS only, defaults to production. Send sandbox for debug and simulator builds — a token minted by one Apple host is rejected by the other, and nothing in the token itself says which. The SDKs report this for you.
Why installation_id and not the device token

FCM rotates device tokens, and auto-invalidates them after roughly 270 days of inactivity. If the token were the key, every rotation would look like a new device — and an opt-out recorded against the old token would be lost.

installation_id is whatever value stays stable for one app installation on one device. When migrating tokens from another provider and you have no such value, derive one deterministically from the token you are importing (for example a SHA-256 of it) so that re-running the import is idempotent.

Transport credentials

ParameterTypeRequired forDescription
device_tokenstringandroid, iosThe FCM registration token on android; the APNs device token on ios. Max 4096 characters.
endpointstringwebWeb Push endpoint URL. Max 2048 characters.
p256dh_keystringwebWeb Push P-256 ECDH public key.
auth_keystringwebWeb Push auth secret.

Device attributes

All optional, all used for targeting and diagnostics.

ParameterTypeDescription
enablement_statusstringOS notification permission as your app last observed it: AUTHORIZED, DENIED, NOT_DETERMINED, PROVISIONAL, UNAUTHORIZED. Omit rather than guess.
sdk_versionstringArsel SDK build on the device, e.g. android/1.2.0. Supply only if the SDK is embedded — see the warning below.
app_versionstringYour app's version.
os_versionstringDevice OS version.
device_modelstringe.g. Pixel 8.
device_manufacturerstringe.g. Google.
device_timezonestringIANA zone, e.g. Asia/Riyadh. Cannot be backfilled later — send it.
device_localestringBCP-47, e.g. ar-SA.
Do not send sdk_version for imported devices

sdk_version is what marks a device as instrumented. Sending it for a device with no embedded Arsel SDK tells Arsel to expect delivery and engagement reports that will never arrive.

Omitting it is what makes a migrated device work correctly: the notification renders natively on the device with no SDK involved.

PROVISIONAL is not a denial

On iOS, PROVISIONAL means notifications are delivered quietly to Notification Centre without ever showing a prompt. Recording those devices as not-permitted undercounts your reachable audience.

{
"email": "john.doe@example.com",
"installation_id": "install-abc123",
"platform": "android",
"device_token": "fzXn2…J3x9",
"enablement_status": "AUTHORIZED",
"device_timezone": "Asia/Riyadh",
"device_locale": "ar-SA"
}

Response

{
"id": "01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a",
"installation_id": "install-abc123",
"status": "ACTIVE",
"contact_id": "01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a",
"opted_out": false,
"device_secret": "…"
}
FieldTypeDescription
idstringDevice ID (UUIDv7)
installation_idstringEchoed back
statusstringACTIVE, EXPIRED, REVOKED, or FAILED
contact_idstringThe contact the device is now bound to
opted_outbooleantrue when a durable opt-out kept the device revoked despite this call
suppressedbooleantrue when the device stayed retired because this call replayed a token the push service has already rejected as dead. Re-registering a dead token never revives it — send the new token the device was issued.
device_secretstringReturned exactly once, at creation. Present only on the call that created the device record.
device_secret is shown once

Only its SHA-256 is stored, so it can never be retrieved again. If your app embeds the SDK, relay this value to the app — it is the device's proof of possession when reporting delivery and engagement. If you are importing devices without an SDK, you can ignore it.


Examples

curl -X POST "https://api.arsel.sa/v1/push/devices" \
-H "Authorization: Bearer be_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"email": "john.doe@example.com",
"installation_id": "install-abc123",
"platform": "android",
"device_token": "fzXn2...J3x9"
}'

Migrating devices from another provider

Because Arsel sends under your own sender identity — your Firebase project on Android, your APNs key on iOS — the tokens you already hold stay valid. There is no re-registration and no app release required.

Check what your iOS export actually contains

An ios device must carry an APNs device token. If your previous provider relayed iOS through Firebase, your export holds FCM registration tokens instead — those mean nothing to Apple and cannot be imported as iOS devices. Those users re-register on their own once they open a build carrying the Arsel SDK.

  1. Export your existing tokens, each with the contact identifier it belongs to.
  2. For each one, call this endpoint with installation_id derived deterministically from the token, and omit sdk_version.
  3. The device is immediately sendable, rendering notifications natively.
  4. When you later ship an app build embedding the Arsel SDK, it adopts the imported row rather than creating a second one.

For anything more than a handful of devices, use Bulk Register Devices instead of looping this route — it takes 1,000 per call, resolves contacts by external_id, and reports per-row errors you can reconcile against your export.


Error Responses

The transport credentials do not match the platform — e.g. platform: "web" with no endpoint:

{
"status_code": 400,
"name": "bad_request",
"message": "Missing transport credentials for the supplied platform."
}