Endpoints
The server plugin exposes these endpoints under endpointPrefix. The default prefix is /cross-device.
| Endpoint | Method | Purpose |
|---|---|---|
/cross-device/start | POST | Start a new order. |
/cross-device/claim | POST | Claim an order from the approving device. |
/cross-device/challenge | POST | Read the challenge envelope. |
/cross-device/approve | POST | Submit a verified proof. |
/cross-device/reject | POST | Reject from the approving device. |
/cross-device/cancel | POST | Cancel from the initiating device. |
/cross-device/finalize | POST | Finalize an approved order. |
/cross-device/events | GET | Subscribe to server-sent order events. |
Start
Creates the order on the initiating device.
await authClient.crossDevice.start({
kind: "login",
adapterId: "passkey",
returnTo: "/dashboard",
displayTitle: "Sign in",
displaySummary: "Approve this sign-in on your phone.",
})
Returns:
| Field | Meaning |
|---|---|
orderId | Public order id used by every later call. |
claimToken | Token embedded in the approving-device claim URL. |
claimUrl | URL you show as a QR code, deep link, or copyable link. |
eventToken | Token for the initiating device event stream. |
desktopToken | Token for cancel and finalize from the initiating device. |
expiresAt | Unix timestamp in milliseconds. |
Claim
Exchanges the claim token for a challenge token on the approving device.
await authClient.crossDevice.claim({
orderId,
claimToken,
})
Returns challengeToken.
Challenge
Reads the challenge envelope that your adapter prepared.
const challenge = await authClient.crossDevice.challenge({
orderId,
challengeToken,
})
The response includes action, nonce, origin, expiresAt, display text, adapter metadata, and the adapter-generated message.
Approve
Submits the adapter proof.
await authClient.crossDevice.approve({
orderId,
challengeToken,
proof,
})
Returns { ok: true, orderId, status: "approved" }.
Reject
Rejects the order from the approving device.
await authClient.crossDevice.reject({
orderId,
challengeToken,
})
Returns { ok: true, orderId, status: "rejected" }.
Cancel
Cancels the order from the initiating device.
await authClient.crossDevice.cancel({
orderId,
desktopToken,
})
Returns { ok: true, orderId, status: "cancelled" }.
Finalize
Consumes an approved order from the initiating device.
await authClient.crossDevice.finalize({
orderId,
desktopToken,
})
For login orders, finalization applies the resolved Better Auth session and may return a token. For sign and transaction orders, finalization returns the approved proofArtifact.
Events
Subscribes to server-sent events for the initiating device.
const source = authClient.crossDevice.subscribe({
orderId,
eventToken,
onEvent(event, payload) {
console.log(event, payload.status)
},
})
Events: claimed, waiting_user, approved, rejected, expired, cancelled, finalized.
Errors
Client helpers return { data, error }. Endpoint errors include a message and may include a Better Auth status code.
| Case | Typical result |
|---|---|
| Missing or wrong token | Unauthorized response. |
| Expired order | Conflict or not-ready response after lifecycle refresh. |
| Invalid proof | Unauthorized response from CrossDeviceProofError. |
| Invalid transition | Bad request or conflict response. |