Theme
Open the portal

How it works

The mental model behind every allus integration — services, connect handles, connections, request vs share fields, consent, one-time vs live data, and the zero-knowledge encryption model. No code; just the concepts you'll build on.

This page is the conceptual foundation. Once the model below clicks, Getting started turns it into a running integration, and the Service API reference / SDKs pages cover the mechanics.

Services

A service is the unit a company connects through — think of it as one integration surface (a CRM, a support desk, a loyalty program). A company can have several. Each service has:

Connecting always happens via a service — never to a company directly. A service accepts connections, and the company-data API answers for it, once your company is on the Basic level or above; below that the service presents as one that does not exist.

Connect handles

People connect using a connect handle: two case-insensitive URL segments, COMPANYCODE/SERVICECODE.

  ACME01           /          CRM
  ───────                     ─────
  the company's              the service's
  profile share code         share code

The company code alone is an error everywhere on the person-side lookup (profile.company_code_requires_service) — a person must always name the service they're connecting to. The handle resolves to a connect screen showing the company + service identity and the list of fields the service requests.

Connections & service connections

Two layers describe a relationship:

  person ──(1)── company connection ──(N)── service connection ──(N)── answered request slots
                  (one per pair)            (one per service)         (the person's field per slot)

From the API you read company connections and, inside each, the per-service answered values. Each connected person is one row in GET /api/company-data/connections; the SDKs surface it as a Connection with a values map keyed by your request slugs.

Request fields, share fields, and the company profile

Data flows in both directions, configured separately:

Request fields — what you ask for

A service's request fields declare what it wants from connecting people, by field type plus an advisory suggested field. Each request field has a stable slug you choose (e.g. work_email) — that slug is your contract and the key every value comes back under. You can rename the label freely; the slug never changes. Request fields carry flags:

Request fields are edited as a draft and published; adding a field to a live service fans out a consent request to existing connections (see below). Which types you may ask for is served, not listed here — every type the platform carries except an option-less choice type — and a row asking for a PARENT type is answered by any type below it. See Field types & the typed value model.

Share fields — what you share back

A service's share fields are company-owned fields you publish to every connected person on that service (a support email, an address, a logo). These are plaintext by design — see the encryption model below.

The company profile

Your company profile is shared with every connection across any of your services (not per-service). It's also plaintext.

You only ever see what you asked for. The response is keyed by your request slugs. The person's underlying field choice — which of their profile fields answers a slot — is dropped server-side and never reaches your code (or even the read API).

Connecting is synchronous — there's no company approval step. The flow, from a person's app:

  1. Resolve handle      GET …/lookup/COMPANYCODE/SERVICECODE
                          → company + service identity, request fields, what the
                            service shares back (plaintext preview)
  2. Decide per slot      pick one of their matching fields, OR type a one-time value,
                          OR a per-company custom field (suggested field pre-selected)
  3. Encrypt              each chosen value → encrypted to YOUR service public key
  4. POST connect         one transaction: pair anchor + service connection +
                          the answered slots; the company's shared data returns
                          instantly in the 201 response

After connecting, the person sees your service shares and company profile immediately, and you start receiving their answers.

If you add request fields to a service that already has connections, each connection gets a pending consent request (one merged item per connection). The person can provide the new fields or decline. Declines are grandfathered: the connection stays fully active even for fields you marked mandatory — mandatory only applies at first-connect time.

One-time vs live (auto-updating) fields

This distinction is the heart of "auto-updating data":

Live (connected)One-time (snapshot)
What it isThe answer is mapped to one of the person's living profile fields.A frozen value captured at answer time.
Updates?Yes — when the person edits that field, a re-encrypted copy is pushed to your service automatically.Never — it's a point-in-time snapshot.
You seelive = true on the value.live = false on the value.
On disconnectRemoved.Removed.

A live value's lifecycle: the person's edit re-encrypts the new plaintext to your service key and emits a field_updated change, and so does the field gaining or losing its verification. A one-time value is written once and never touched again. The SDKs expose this as the live flag on each value.

The zero-knowledge encryption model

This is the most important concept to internalize. Data direction decides everything:

DirectionEncryptionWho encrypts
Person → service (the values you receive)Encrypted to your service public keyThe person's client, before the value ever leaves their device
Company → person (share fields + company profile)Plaintextn/a — stored as plain values
  PERSON's device                       allus platform                 YOUR backend
  ┌──────────────┐                      ┌──────────────┐               ┌──────────────┐
  │  plaintext   │                      │              │               │   plaintext  │
  │  "alice@..." │ ──encrypt(svc pub)──▶│  CIPHERTEXT  │──ciphertext──▶│  via the SDK  │
  │              │                      │  {"_enc":1,…}│               │  decrypt with │
  └──────────────┘                      │  (can't read)│               │  service priv │
                                        └──────────────┘               └──────────────┘

Concretely:

There's also a company account key, separate from every service key. It encrypts webhook payloads when you enable encrypt_payload (see Webhooks), and it is the key another company encrypts to when your company connects to its service as a customer (Account automation clients). You download its private half in the portal under Settings → Account key.

You never handle decryption directly. The SDKs do it for you — loading the PEM, unwrapping the AES key, decrypting the payload, parsing the typed value. The Reference page documents the raw wrapper format for non-SDK integrators.

Ward / parental gating

Some allme accounts are wards with active parental controls. When a ward connects to your service (or answers a new consent request), the action doesn't take effect immediately — it returns a pending state and a custodian must approve it. From your side this is transparent: you simply see the connection or values appear once a custodian approves. Declining a consent is never gated. You don't implement anything for this; it's handled entirely on the allme side.

Putting it together

You define request fields once. People connect via your handle and choose how to answer each slot, encrypting to your service key. You receive their answers — live ones keep updating — keyed by your slugs, decrypted by the SDK, while the platform never sees the plaintext. Next: