Architecture Overview

The client-service is the backend for the Emarsys Mobile Engage SDK. It manages client registration, contact identification, push tokens, and geofences. The service consists of a web process serving REST APIs (v3 and v4) and several Pub/Sub worker processes that handle asynchronous workflows like contact resolution.

System Overview

graph TB SDK[Mobile SDK] subgraph me-client-service Web[Web Server
Fastify :3000] Workers[Pub/Sub Workers] end PubSub[Cloud Pub/Sub] Dynamo[(DynamoDB)] Redis[(Redis)] ExtAPIs[Internal APIs
Push / Contact Data / Predict / SDK Mgmt] SecretMgr[Secret Manager] AMQP[RabbitMQ] SDK -->|REST| Web Web -->|read/write| Dynamo Web -->|publish| PubSub Web -->|fetch| ExtAPIs Web -->|cache / rate-limit| Redis Web -->|secrets| SecretMgr PubSub -->|subscribe| Workers Workers -->|write| Dynamo Workers -->|resolve| ExtAPIs Workers -->|publish| AMQP

Endpoint Flows

Client Registration

POST /v3/apps/:appCode/client
POST /v4/apps/:appCode/client

Registers or updates a client device. V4 additionally creates an anonymous contact on first registration.

sequenceDiagram participant SDK participant Web participant DynamoDB participant PubSub participant ContactRefMgr as Contact Ref Manager SDK->>Web: POST /client (platform, SDK version, device info) Web->>DynamoDB: upsert device Web->>PubSub: publish client updates Web->>PubSub: publish client changes opt V4 — no existing contact Web->>ContactRefMgr: create anonymous contact reference Web->>PubSub: publish anonymous contact change end Web-->>SDK: client state token

Push Token

PUT /v3|v4/apps/:appCode/client/push-token
DELETE /v3|v4/apps/:appCode/client/push-token

Sets or clears the push notification token for a device.

sequenceDiagram participant SDK participant Web participant DynamoDB participant PubSub SDK->>Web: PUT or DELETE /push-token Web->>DynamoDB: set or clear push token Web->>PubSub: publish push token update Web->>PubSub: publish client changes Web-->>SDK: client state token

Set Identified Contact

POST /v3|v4/apps/:appCode/client/contact

Sets an identified contact on the client. The contact-id is resolved asynchronously by workers. V4 can optionally wait for the resolution via Redis.

sequenceDiagram participant SDK participant Web participant ContactRefMgr as Contact Ref Manager participant PredictIDs as Predict IDs participant PubSub participant Worker as Identified Contact Resolver participant ContactData as Contact Data API participant CIDWriter as Contact ID Writer participant DynamoDB participant Redis SDK->>Web: POST /contact (fieldId + fieldValue) Web->>ContactRefMgr: get or create contact reference Web->>PredictIDs: fetch predict IDs Web->>PubSub: publish contact field change Web-->>SDK: contact token + client state token Note over PubSub,DynamoDB: Async worker pipeline PubSub->>Worker: contact field change message Worker->>ContactData: resolve contact-id from field Worker->>PubSub: publish contact-id change PubSub->>CIDWriter: contact-id change message CIDWriter->>DynamoDB: write contact-id to device CIDWriter->>ContactRefMgr: link reference to contact key CIDWriter->>Redis: notify contact-id resolved opt V4 — wait for resolution SDK->>Web: POST /contact (with Prefer: wait header) Web->>Redis: wait for contact-id notification Web-->>SDK: contact token (with hasContactId) end

Contact Token Refresh

POST /v3|v4/apps/:appCode/client/contact-token

Refreshes an expiring contact token. V4 can optionally wait for contact-id resolution.

sequenceDiagram participant SDK participant Web participant ContactRefMgr as Contact Ref Manager participant PredictIDs as Predict IDs SDK->>Web: POST /contact-token (refresh token) Web->>Web: decrypt refresh token Web->>ContactRefMgr: lookup contact reference Web->>PredictIDs: fetch predict IDs Web-->>SDK: new contact token + client state token opt V4 — wait for contact-id Web->>Web: resolveContactId (Redis wait) Web-->>SDK: contact token (with hasContactId) end

Appless Contact Token

POST /v3/contact-token
DELETE /v3/contact-token

Creates or deletes a contact token without an application context, operating at the merchant level.

sequenceDiagram participant SDK participant Web participant ContactRefMgr as Contact Ref Manager participant PredictIDs as Predict IDs participant PubSub participant Worker as Appless Contact Resolver SDK->>Web: POST /contact-token (fieldId + fieldValue) Web->>ContactRefMgr: get or create contact reference (no app) Web->>PredictIDs: fetch predict IDs opt contact-id not yet resolved Web->>PubSub: publish appless contact resolution PubSub->>Worker: resolve contact Worker->>ContactRefMgr: resolve and link contact end Web-->>SDK: contact token + refresh token

App Switching (v4)

POST /v4/apps/:appCode/client/app

Switches a client from one application to another, resetting device state in the source app and re-registering in the target app.

sequenceDiagram participant SDK participant Web participant SdkMgmt as SDK Management API participant DynamoDB participant PubSub participant ContactRefMgr as Contact Ref Manager SDK->>Web: POST /app (targetAppCode) Web->>SdkMgmt: validate target app exists Web->>DynamoDB: reset device in source app Web->>PubSub: publish reset push token Web->>DynamoDB: upsert device in target app Web->>PubSub: publish client updates (target app) Web->>ContactRefMgr: create anonymous contact (target app) Web->>PubSub: publish contact change (target app) Web-->>SDK: new client state token + contact tokens

Geo-Fences

GET /v3|v4/apps/:appCode/geo-fences

Returns geo-fence locations for the application, using ETag-based caching.

sequenceDiagram participant SDK participant Web participant Cache as Cache (Memory / Redis) SDK->>Web: GET /geo-fences (If-None-Match: etag) Web->>Cache: lookup geo-fences for app alt ETag matches Web-->>SDK: 304 Not Modified else new data Web-->>SDK: 200 + geo-locations (ETag header) else not found Web-->>SDK: 204 No Content (v4) / 404 (v3) end

Component Details

Web Process

The main Fastify HTTP server exposes both v3 and v4 REST APIs on port 3000. Each request passes through a lifecycle of hooks that fetch application metadata, validate the app-code and client-id, and on response publish any state changes and log SDK interactions.

See Processes for details on outputs.

Workers

All workers consume messages from Google Cloud Pub/Sub subscriptions on the client-changes topic, filtered by message attributes. They handle the asynchronous parts of contact resolution and state propagation.

Worker Responsibility

Identified Contact Resolver

Resolves contact-id from contact-field for known contacts via Contact Data API.

Anonymous Contact Resolver

Resolves or creates anonymous contacts via Contact Data API.

Appless Contact Resolver

Resolves contacts without an app context.

Contact ID Writer

Writes resolved contact-id to DynamoDB and publishes to downstream topics.

Contact ID Writer (Expired)

Handles expired contact-id write retries.

Suite Contact Updater

Publishes contact updates to RabbitMQ for legacy Suite integration.

Dynamo Clients Maintainer

Maintains client state consistency in DynamoDB.

External Dependencies

Dependency Type Usage

Google Cloud Pub/Sub

Message broker

Event distribution between web server and workers. Topics: client-changes, client-state-client-updates, client-state-push-token-updates, client-state-contact-updates.

AWS DynamoDB

Database

Primary storage for client state and device information.

Redis (3 clusters)

Cache / Rate limit

push-redis for SDK object logs, inapp-redis for in-app data, rate-limit-redis for request rate limiting.

Google Secret Manager

Secrets

JWT signing key management.

Push Service (me-push-web)

Internal API

Application configuration and push settings. Results cached in memory and Redis.

Contact Data API

Internal API

Contact field resolution and contact-id lookup.

SDK Management API

Internal API

Application metadata.

Predict Settings

Internal API

Prediction configuration per customer.

Account Data API

Internal API

Account-level data.

RabbitMQ

Message broker

Legacy Suite contact update integration via me-publisher-sdk queue.

Caching

Application data is cached in two levels: in-memory (L1) and Redis (L2) with a default TTL of 24h. See Caching for invalidation details.

Tokens

The service manages JWT tokens for client state (X-Client-State / EC-Client-State) and contact identity (X-Contact-Token / EC-Contact). See Tokens for details.