Skip to main content
The external app magic link lets you email a customer a link that opens already authenticated in your app through a Branch.io deep link, instead of routing them through the Profile Portal UI. Your backend triggers the call; the customer receives an email whose link is built on your app URL with a short-lived Omneo ID token and its expiry embedded. This builds on the same profile-scoped token model as Using Omneo ID. Trigger the endpoint from a trusted server, never from the browser.

How it works

1

Your backend calls the endpoint

Your server computes a shared-secret HMAC over the customer’s email and calls POST /api/auth/externalMagicLink with a valid Omneo bearer token.
2

Omneo verifies the request

Omneo checks the bearer token, confirms the calling origin is allow-listed, and matches the request to your configured app entry using the supplied secret.
3

Omneo mints a token and emails the link

When the profile exists, Omneo mints a profile-scoped ID token, renders your link template with the token and expiry, and emails the link to the customer.
4

Your app validates the token

The customer opens the link in your app. Your app decodes the embedded token, checks the expiry, and uses it as a bearer token against the Omneo ID service.

Endpoint

POST /api/auth/externalMagicLink

Headers

HeaderRequiredNotes
AuthorizationYesBearer <omneo-token>. Validated against the Omneo auth service.
OriginYesYour calling origin must be allow-listed by Omneo.

Body

FieldRequiredDescription
emailYesEmail of the profile to authenticate.
appUrlYesYour app base URL (origin). Must match a configured app entry.
secretYesHMAC-SHA256 of the email, keyed with your shared secret. See Computing the secret.
redirectNoIn-app redirect hint. Defaults to /home.

Responses

StatusMeaning
200{ data: { type: 'existing', id, email } } when the profile exists; the link is emailed. { data: { type: 'new', email } } when no profile matches the email.
400Missing email, appUrl, or secret.
401Missing Authorization header.
403Invalid or expired bearer token, a disallowed Origin, or an appUrl/secret that does not match a configured app.
502Token generation failed upstream.

Computing the secret

The bearer token alone does not authorise the call. You are also issued a shared secret and must prove you hold it on every request. Compute the value you send like this:
secret = HMAC-SHA256(message = email, key = sharedSecret)
Send the result as the secret body field. Omneo recomputes the same HMAC over the email it received and accepts the request only if the two match. Binding the HMAC to the email stops a captured secret from being replayed to request a link for a different profile. Use the exact email string you send in the payload, with the same casing and trimming:
node -e 'console.log(
  require("crypto").createHmac("sha256", "<shared-secret>")
    .update("user@example.com").digest("hex"))'
Keep the shared secret on your server. It is the HMAC key and is never sent in the request body.

Configuration

Your app entry is configured by Omneo during onboarding. You provide your app origin and a Branch.io link template, and Omneo issues you the shared secret. Each entry has three values:
ValueDescription
baseUrlThe origin you send as appUrl. Requests are matched to an entry by comparing origins.
linkTemplateThe full template used to build the emailed link. Omneo controls this, so you only send your origin.
secretThe shared secret (HMAC key) issued to you.
{
  "baseUrl": "https://shoesandsox.app.link",
  "linkTemplate": "https://shoesandsox.app.link/auth/callback?token={{token}}&iFrame=true&expiry={{expiry}}&redirect={{redirect}}",
  "secret": "<the-shared-secret-issued-to-you>"
}
The template is rendered with the variables token, expiry, and redirect. The example above renders to:
https://shoesandsox.app.link/auth/callback?token=<base64-jwt>&iFrame=true&expiry=<unix-seconds>&redirect=/home
Ensure the rendered token and redirect values are URL-encoded (or use a base64url-safe token) so characters like +, /, and = survive query-string parsing. Use double braces, for example {{token}}, in the template. A template with no placeholders falls back to appending ?token=...&expiry=....

Validating the ID token on app open

The token query parameter is a base64-encoded JWT. When the customer opens the link, your app should:
  1. Base64-decode the token value, then JWT-decode it.
  2. Read the pid (profile ID) and exp (unix expiry) claims.
  3. Reject the link if exp is in the past. Links are valid for about 24 hours.
  4. Use the decoded JWT as a bearer token against the Omneo ID service, for example GET https://api.[tenant].getomneo.com/id/api/v1/profiles/me.

Refreshing the token

The embedded ID token is short-lived. To obtain a fresh token, mint one server-side so the Omneo bearer token never reaches the client:
POST https://api.[tenant].getomneo.com/id/api/v1/auth/token
Authorization: Bearer <your-omneo-token>
Content-Type: application/json

{ "id": "<profile-id>" }
{ "data": { "token": "<jwt>" } }
This is the same profile-scoped token flow covered in Using Omneo ID. Hand the returned token to your app the same way you handle the token embedded in the magic link.