Server-side enrollment
If you own your own user registration — your users sign up inside your app, not on an AKIN surface — you can enrol them in AKIN loyalty server-to-server, at registration time, without the member ever seeing an AKIN login.
This is a single mutation, enrollMember, authorised on your x-partner-api-key. It creates (or finds) the AKIN member, enrols them in your program, and returns the AKIN member id for you to persist.
Requires a secret key with
member:write. Enrolling a member is a write, so it needs a secret (server-only) key carrying themember:writescope — a publishable/browser-safe or read-only key is rejected. Grant the scope when you create the key in the dashboard, and keep the key on your server. See publishable vs secret keys.
On Node? The typed Server SDK exposes this as
akin.enrollMember(...)— typed input/result, structured errors, and rate-limit handling — instead of hand-rolling the request below.
When to use this
| You want… | Use |
|---|---|
| To enrol a user you just registered in your own app | enrollMember (this page) |
| The member to sign in on an AKIN surface (magic link, passkey) | The authentication flows |
enrollMember is headless: no magic link, no email, no password — the member is never prompted to do anything. If that same member later signs in on an AKIN surface with the same email, they land on the same account (accounts are keyed by email), so there is never a duplicate.
The mutation
mutation EnrollMember($input: EnrollMemberInput!) {
enrollMember(input: {
email: "ada@example.com"
firstName: "Ada"
lastName: "Lovelace"
externalId: "your-loyalty-id-123" # optional
}) {
memberId # AKIN member id — persist this
created # true if a new AKIN account was created
enrollmentChanged # true if this enrolment was new (vs an idempotent replay)
}
}Send it with your partner API key:
curl https://api.akintravel.com/graphql \
-H "Content-Type: application/json" \
-H "x-partner-api-key: $AKIN_PARTNER_API_KEY" \
-d '{
"query": "mutation($i: EnrollMemberInput!){ enrollMember(input:$i){ memberId created enrollmentChanged } }",
"variables": { "i": { "email": "ada@example.com", "firstName": "Ada", "lastName": "Lovelace", "externalId": "your-loyalty-id-123" } }
}'Input
| Field | Required | Notes |
|---|---|---|
email | yes | Matched case-insensitively for idempotency. |
firstName | yes | Used only when creating a new account. |
lastName | yes | Used only when creating a new account. |
externalId | no | Your own loyalty key for this member. Stored on the enrolment and unique within your partner — re-send it to correct a mapping. Lets you look the member up later by your own id. |
There is no partnerId field — the partner is always derived from your API key. You cannot enrol a member into another partner’s program, and you cannot set a member’s tier, points, or status (those are AKIN-controlled).
Idempotency
enrollMember is safe to call repeatedly for the same (your partner, email):
- If the email already maps to an AKIN member, that member is reused —
createdisfalseand the existingmemberIdis returned. No profile fields are overwritten. - If the member is already enrolled in your program,
enrollmentChangedisfalse. The call still succeeds and returns the samememberId. - A re-send that omits
externalIdnever clears a previously-stored one; a re-send that includes a differentexternalIdupdates it.
This means you can call it on every registration (and on retries) without risk of duplicates.
Webhooks
A fresh account fires member.created; a new (or reactivated) enrolment fires member.enrolled. Idempotent replays that change nothing are silent. See Webhooks.