---
title: "Exchange a magic-link token for an access token (passwordless login)."
method: POST
path: "/api/v1/tokens/magic-link"
tags: ["Public"]
---

# Exchange a magic-link token for an access token (passwordless login).

`POST /api/v1/tokens/magic-link`

## Request

- Base URL: `https://go.outseta.com`
- URL: `https://go.outseta.com/api/v1/tokens/magic-link`
- Auth: none declared

Call this with the magicToken carried by the link from a
"magic link" email (requested via POST /api/v1/crm/people/requestMagicLink):
            
    { "magic_token": "eyJ..." }
            
The link proves control of the email inbox, which satisfies the first
factor. The response then matches POST /api/v1/tokens:
            
 - 200 with an access token when no further factor is required;
 - 202 with a two_factor_required challenge when the user
   has a non-email second factor (e.g. an authenticator app) that must
   still be cleared via POST /api/v1/tokens/two-factor. Email is
   never offered as that second factor — it was already the first.
            
An invalid, expired, already-used token (or a tenant without magic-link
login enabled) returns 400 (invalid_grant).

## Query parameters

- `data` unknown, required

## Response `200`

The link was valid; the access token (JWT) is returned.

- TokenPayload
  - `access_token` string, nullable
  - `authentication_callback_url` string, nullable
  - `expires_in` integer
  - `id_token` string, nullable
  - `refresh_token` string, nullable
  - `token_type` string, nullable

## Other responses

- `202`: A second factor must be completed before a token is issued.
- `400`: Invalid, expired or already-used link (`invalid_grant`).

---

[API](https://skmtc.dev/outseta/apis/outseta-api.md) · [All operations](https://skmtc.dev/outseta/apis/outseta-api/llms.txt) · [OpenAPI document](https://skmtc.dev/outseta/apis/outseta-api/revisions/c362823af82d?raw)
