PUBLIC
Starts the magic link login flow for an end user. Sends a one-time login link
to the provided email address. The link contains a challenge token to be passed
to POST /auth/link/verify.
Events fired
| Event | Trigger | Description |
|---|---|---|
authorization.CodeIssued | sync | Fired when the magic link code is successfully issued. |
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
Events —authorization.CodeIssued
Why this endpoint exists
Session exchange works when your customers are already signed in on your platform. When they are not — a standalone storefront, a campaign where you have no accounts of your own — the platform has to establish identity itself.
It does that with a magic link: the customer types their e-mail, receives a one-time link, clicks it, and lands back on your storefront with a session. No password to create, remember or reset.
If you have your own accounts, use session exchange instead. It is fewer steps for the customer, has no e-mail deliverability risk, and does not depend on someone reading their inbox mid-purchase.
Request
curl -X POST https://api.acc.funtrips.io/v2/auth/link/create \
-H "x-campaign-id: $CAMPAIGN_ID" \
-H "Accept-Language: nl-NL" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"callback_url": "https://tickets.your-brand.example/auth/callback",
"query_params": { "returnTo": "/merchant/amsterdam-zoo" },
"integrator_params": { "tier": "gold" }
}'| Field | Type | Required | Notes |
|---|---|---|---|
email | Yes | Where the link is sent | |
callback_url | uri | No | Where the link points. Must be permitted for the campaign |
query_params | object | No | String values appended to the callback URL |
integrator_params | object | No | Opaque values carried on the session JWT. Max 2 KiB encoded |
callback_url must be allowed
callback_url must be allowedThe platform appends the challenge token and session to this URL. It is validated against the campaign's configured hosts — an unregistered URL is rejected with user-no-callback-url, because otherwise the endpoint would be an open redirect that mails attacker-controlled links to arbitrary addresses.
Register your callback hosts with us before you use them. Omit the field and the campaign's default is used.
query_params preserves intent
query_params preserves intentValues here are appended to the callback URL, so the customer returns to where they were rather than to your homepage. Put the product or merchant they were looking at in here — the round trip through an inbox is exactly where purchase intent gets lost.
integrator_params rides on the session
integrator_params rides on the sessionAn opaque map carried as a single integrator_params claim on every subsequent authenticated request, consumed by downstream infrastructure such as partner loyalty mirroring. The API never interprets the keys.
Keep the JSON-encoded total within 2 KiB — it is on the JWT, and an oversized claim breaks every request the session makes.
Response
200 OK — the link has been e-mailed.
{
"data": { "expires_in_seconds": 900 },
"meta": { "correlation_id": "…" }
}expires_in_seconds is how long the emailed token stays valid. Show it: "we sent you a link, it works for 15 minutes" sets the right expectation and cuts support contacts.
The response says nothing about the customer
A 200 means we accepted the request and sent an e-mail. It does not mean the address exists, is deliverable, or belongs to a real customer.
That is on purpose: an endpoint that reported "no such user" would let anyone enumerate your customer base one address at a time. So the response is identical for a known and an unknown address, and your UI must say "check your inbox" rather than anything that implies the account was found.
Localisation
The e-mail body is localised from Accept-Language, negotiated against the campaign's supported locales, with your campaign's branding applied on top. Send the customer's language — this e-mail is often their first contact with the storefront.
Errors
| Status | type | What to do |
|---|---|---|
400 | — | email missing or malformed |
400 | user-no-callback-url | callback_url is not permitted for this campaign |
400 | campaign-not-found | Unknown campaign |
502 | user-magic-link-send-failed | The e-mail could not be sent. Retryable |
500 | — | Unexpected fault. Retryable |
A 502 is the one to surface: the customer is waiting for an e-mail that will never arrive. Offer a "resend" affordance rather than leaving them on "check your inbox".
Next step
The customer clicks the link, arrives at your callback_url with a token in the query string, and your front-end posts it to POST /auth/link/verify.
Notes
- Rate-limit your own form. An open e-mail field is an abuse vector — it can be driven to mail-bomb a third party. Debounce submissions and cap attempts per address before calling this endpoint.
authorization.CodeIssuedis published when the code is issued.
