Sign-in and session API reference

Updated

20 operations of the Fax for Agents API, from the OpenAPI document version 1.20.0, snapshot of 2026-11-13. The base URL is https://api.faxforagents.com/v1. This is one group of the full reference.

  • POST /v1/auth/magic-links always answers 202, whether or not the address has an account.
  • Idempotency-Key is accepted but ignored on the session-only authentication actions.
  • Accepting an invitation needs the signed-in email to match the invited one.

Operations

Who is the auth group for?

For a browser. These operations sign a person in to the dashboard and manage that person's sessions and passkeys. None of them takes an API key. A script that sends faxes authenticates with a key from the identity group and never calls this group.

The group is in the public document because the dashboard is a client of the same API. Reading it tells you what a session is and what a sensitive action asks of a person.

Which sign-in methods are there?

GET /v1/auth/providers says which methods are on: magic links, passkeys, GitHub and Google. It takes no credential.

A magic link starts with POST /v1/auth/magic-links and an email address. The answer is always 202, so the call does not reveal whether an address has an account. Opening the link, GET /v1/auth/magic-links/{token}, sets the session cookie and redirects to the app.

A passkey sign-in is two calls. POST /v1/auth/passkeys/login/options returns the options for navigator.credentials.get(), discoverable, so no email is typed. POST /v1/auth/passkeys/login/verify takes the assertion and signs the person in.

GitHub and Google use GET /v1/auth/oauth/{provider}/start, which redirects to the provider, and /callback, which finishes the sign-in. Both answer 503 when the provider is not configured.

What does a signed-in session carry?

GET /v1/auth/me returns the person, the organization being looked at, the other organizations, the passkeys, the providers and the CSRF token. POST /v1/auth/switch-organization changes which organization the session looks at. POST /v1/auth/logout ends the session.

A person can read the live sessions with GET /v1/auth/sessions and revoke one by id. DELETE /v1/auth/sessions revokes every other one, which the summary calls "sign out everywhere else".

What is re-authentication for?

Some actions need proof that the person at the keyboard is still the account's owner: approving a send, registering a passkey, turning on auto-send. POST /v1/auth/reauth gives that proof. With a passkey it answers 200 at once. With the mail method it answers 202 and sends a fresh link.

Registering a passkey is POST /v1/auth/passkeys/register/options, which needs a fresh session, then /register/verify with the credential the browser created. GET /v1/auth/passkeys lists a person's passkeys and DELETE removes one.

How do invitations and reviewers sign in?

POST /v1/auth/invitations/{token}/accept joins the organization an invitation link names. The signed-in email has to match the invited address. The invitation itself is created in the members group.

POST /v1/auth/review-access signs in with reviewer credentials, an email and a code. It takes no session and answers 401 when the pair is wrong.

Why do these calls ignore Idempotency-Key?

The OpenAPI document repeats one sentence on these operations. The header is accepted but ignored for a session-only authentication action. A repeat creates no customer resource and no charge. Authentication, one-time challenge checks and rate limits still apply. So a retried sign-in call is safe, and a one-time link or challenge still works once.

Each operation in detail

POST /v1/auth/invitations/{token}/accept

Join the organization an invitation link names (the signed-in email must match)

Credential: a signed-in session.

Parameters

POST /v1/auth/invitations/{token}/accept parameters
NameInTypeRequiredNotes
tokenpathstringyes
Idempotency-KeyheaderstringnoReplay the first successful response for the same request within 24 hours. Reusing the idempotency header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay.

Responses

POST /v1/auth/invitations/{token}/accept responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
404not_found: no such resource for this credential. A resource of another organization answers the same.
409The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

POST /v1/auth/invitations/{token}/accept response fields
FieldTypeRequiredDescription
organizationobject OrganizationSummaryyes

Example

curl https://api.faxforagents.com/v1/auth/invitations/TOKEN/accept \
  -X POST

POST /v1/auth/logout

End this session

Idempotency-Key is accepted but ignored for this session-only authentication action. Repeated requests do not create customer resources or charges. Authentication, one-time challenge checks and rate limits still apply.

Credential: a signed-in session.

Responses

POST /v1/auth/logout responses
StatusMeaning
204Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Example

curl https://api.faxforagents.com/v1/auth/logout \
  -X POST

GET /v1/auth/me

The signed-in person, their organizations and the CSRF token

Credential: a signed-in session.

Responses

GET /v1/auth/me responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

GET /v1/auth/me response fields
FieldTypeRequiredDescription
csrf_tokenstringyes
default_environmentstring (live, test)noThe environment the dashboard opens on: live once the organization has a funded wallet or a paid one-time order, else test.
organizationobject OrganizationSummary or nullyes
organizationsarray of object OrganizationSummaryyes
passkeysintegeryes
providersobject Providersyes
sessionobject SessionInfoyes
userobject UserOutyes

Example

curl https://api.faxforagents.com/v1/auth/me

GET /v1/auth/oauth/{provider}/callback

Finish GitHub or Google sign-in (the app forwards its /auth/{provider}/callback here)

Credential: none.

Parameters

GET /v1/auth/oauth/{provider}/callback parameters
NameInTypeRequiredNotes
providerpathstringyes
codequerystringno Default "".
statequerystringno Default "".
errorquerystring or nullno

Responses

GET /v1/auth/oauth/{provider}/callback responses
StatusMeaning
200Successful Response
400The request cannot be read or is refused as sent, for example invalid_request.
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
404not_found: no such resource for this credential. A resource of another organization answers the same.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.
503Something this call depends on is not available or not set up here, for example billing_unavailable or provider_unavailable. error.retryable says whether to send the same call again.

Response fields

GET /v1/auth/oauth/{provider}/callback response fields
FieldTypeRequiredDescription
csrf_tokenstringyes
default_environmentstring (live, test)noThe environment the dashboard opens on: live once the organization has a funded wallet or a paid one-time order, else test.
organizationobject OrganizationSummary or nullyes
organizationsarray of object OrganizationSummaryyes
passkeysintegeryes
providersobject Providersyes
sessionobject SessionInfoyes
userobject UserOutyes

Example

curl https://api.faxforagents.com/v1/auth/oauth/PROVIDER/callback

GET /v1/auth/oauth/{provider}/start

Begin GitHub or Google sign-in (redirects to the provider)

Credential: none.

Parameters

GET /v1/auth/oauth/{provider}/start parameters
NameInTypeRequiredNotes
providerpathstringyes
nextquerystring or nullno
refquerystring or nullnoA referral code from the link.
subquerystring or nullnoThe referral link's tag.

Responses

GET /v1/auth/oauth/{provider}/start responses
StatusMeaning
303Successful Response
404not_found: no such resource for this credential. A resource of another organization answers the same.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.
503Something this call depends on is not available or not set up here, for example billing_unavailable or provider_unavailable. error.retryable says whether to send the same call again.

Example

curl https://api.faxforagents.com/v1/auth/oauth/PROVIDER/start

GET /v1/auth/passkeys

Your passkeys

Credential: a signed-in session.

Responses

GET /v1/auth/passkeys responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

GET /v1/auth/passkeys response fields
FieldTypeRequiredDescription
itemsarray of object Passkeyyes

Example

curl https://api.faxforagents.com/v1/auth/passkeys

POST /v1/auth/passkeys/login/options

Options for navigator.credentials.get() (discoverable, no email needed)

Idempotency-Key is accepted but ignored for this session-only authentication action. Repeated requests do not create customer resources or charges. Authentication, one-time challenge checks and rate limits still apply.

Credential: none.

Responses

POST /v1/auth/passkeys/login/options responses
StatusMeaning
200Successful Response
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Example

curl https://api.faxforagents.com/v1/auth/passkeys/login/options \
  -X POST

POST /v1/auth/passkeys/login/verify

Sign in with a passkey assertion

Idempotency-Key is accepted but ignored for this session-only authentication action. Repeated requests do not create customer resources or charges. Authentication, one-time challenge checks and rate limits still apply.

Credential: none.

Request body application/json, required

POST /v1/auth/passkeys/login/verify request body
FieldTypeRequiredDescription
credentialobjectyes

Responses

POST /v1/auth/passkeys/login/verify responses
StatusMeaning
200Successful Response
400The request cannot be read or is refused as sent, for example invalid_request.
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

POST /v1/auth/passkeys/login/verify response fields
FieldTypeRequiredDescription
csrf_tokenstringyes
default_environmentstring (live, test)noThe environment the dashboard opens on: live once the organization has a funded wallet or a paid one-time order, else test.
organizationobject OrganizationSummary or nullyes
organizationsarray of object OrganizationSummaryyes
passkeysintegeryes
providersobject Providersyes
sessionobject SessionInfoyes
userobject UserOutyes

Example

curl https://api.faxforagents.com/v1/auth/passkeys/login/verify \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"credential":{}}'

POST /v1/auth/passkeys/register/options

Options for navigator.credentials.create() (fresh session required)

Idempotency-Key is accepted but ignored for this session-only authentication action. Repeated requests do not create customer resources or charges. Authentication, one-time challenge checks and rate limits still apply.

Credential: a signed-in session.

Responses

POST /v1/auth/passkeys/register/options responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Example

curl https://api.faxforagents.com/v1/auth/passkeys/register/options \
  -X POST

POST /v1/auth/passkeys/register/verify

Store a new passkey after the browser created it

Credential: a signed-in session.

Parameters

POST /v1/auth/passkeys/register/verify parameters
NameInTypeRequiredNotes
Idempotency-KeyheaderstringnoReplay the first successful response for the same request within 24 hours. Reusing the idempotency header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay.

Request body application/json, required

POST /v1/auth/passkeys/register/verify request body
FieldTypeRequiredDescription
credentialobjectyes
labelstring or nullno

Responses

POST /v1/auth/passkeys/register/verify responses
StatusMeaning
201Successful Response
400The request cannot be read or is refused as sent, for example invalid_request.
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
409The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

POST /v1/auth/passkeys/register/verify response fields
FieldTypeRequiredDescription
aaguidstring or nullyes
backup_eligiblebooleanyes
created_atstringyes
idstringyes
labelstring or nullyes
last_used_atstring or nullyes
transportsarray of string or nullyes

Example

curl https://api.faxforagents.com/v1/auth/passkeys/register/verify \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"credential":{}}'

DELETE /v1/auth/passkeys/{passkey_id}

Remove one of your passkeys

Credential: a signed-in session.

Parameters

DELETE /v1/auth/passkeys/{passkey_id} parameters
NameInTypeRequiredNotes
passkey_idpathstringyes
Idempotency-KeyheaderstringnoReplay the first successful response for the same request within 24 hours. Reusing the idempotency header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay.

Responses

DELETE /v1/auth/passkeys/{passkey_id} responses
StatusMeaning
204Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
404not_found: no such resource for this credential. A resource of another organization answers the same.
409The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Example

curl https://api.faxforagents.com/v1/auth/passkeys/PASSKEY_ID \
  -X DELETE

GET /v1/auth/providers

Which sign-in methods are available

Credential: none.

Responses

GET /v1/auth/providers responses
StatusMeaning
200Successful Response

Response fields

GET /v1/auth/providers response fields
FieldTypeRequiredDescription
githubbooleanyes
googlebooleanyes
magic_linkbooleanno
passkeysbooleanno

Example

curl https://api.faxforagents.com/v1/auth/providers

POST /v1/auth/reauth

Prove it is you again (passkey now, or a fresh link by mail) before a sensitive action

Idempotency-Key is accepted but ignored for this session-only authentication action. Repeated requests do not create customer resources or charges. Authentication, one-time challenge checks and rate limits still apply.

Credential: a signed-in session.

Request body application/json, required

POST /v1/auth/reauth request body
FieldTypeRequiredDescription
credentialobject or nullno
methodstring (passkey, magic_link)yes

Responses

POST /v1/auth/reauth responses
StatusMeaning
200Successful Response
202A sign-in link was sent; opening it in this browser refreshes the session.
400The request cannot be read or is refused as sent, for example invalid_request.
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

POST /v1/auth/reauth response fields
FieldTypeRequiredDescription
csrf_tokenstringyes
default_environmentstring (live, test)noThe environment the dashboard opens on: live once the organization has a funded wallet or a paid one-time order, else test.
organizationobject OrganizationSummary or nullyes
organizationsarray of object OrganizationSummaryyes
passkeysintegeryes
providersobject Providersyes
sessionobject SessionInfoyes
userobject UserOutyes

Example

curl https://api.faxforagents.com/v1/auth/reauth \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"method":"passkey"}'

POST /v1/auth/review-access

Sign in with reviewer credentials

Idempotency-Key is accepted but ignored for this session-only authentication action. Repeated requests do not create customer resources or charges. Authentication, one-time challenge checks and rate limits still apply.

Credential: none.

Request body application/json, required

POST /v1/auth/review-access request body
FieldTypeRequiredDescription
codestring (password)yes
emailstring (email)yes

Responses

POST /v1/auth/review-access responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

POST /v1/auth/review-access response fields
FieldTypeRequiredDescription
csrf_tokenstringyes
default_environmentstring (live, test)noThe environment the dashboard opens on: live once the organization has a funded wallet or a paid one-time order, else test.
organizationobject OrganizationSummary or nullyes
organizationsarray of object OrganizationSummaryyes
passkeysintegeryes
providersobject Providersyes
sessionobject SessionInfoyes
userobject UserOutyes

Example

curl https://api.faxforagents.com/v1/auth/review-access \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"email":"reader@example.com","code":"example"}'

DELETE /v1/auth/sessions

Sign out everywhere else

Idempotency-Key is accepted but ignored for this session-only authentication action. Repeated requests do not create customer resources or charges. Authentication, one-time challenge checks and rate limits still apply.

Credential: a signed-in session.

Responses

DELETE /v1/auth/sessions responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

DELETE /v1/auth/sessions response fields
FieldTypeRequiredDescription
revokedintegeryes

Example

curl https://api.faxforagents.com/v1/auth/sessions \
  -X DELETE

GET /v1/auth/sessions

Your live sessions

Credential: a signed-in session.

Responses

GET /v1/auth/sessions responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

GET /v1/auth/sessions response fields
FieldTypeRequiredDescription
itemsarray of object SessionOutyes

Example

curl https://api.faxforagents.com/v1/auth/sessions

DELETE /v1/auth/sessions/{session_id}

Revoke one of your sessions

Idempotency-Key is accepted but ignored for this session-only authentication action. Repeated requests do not create customer resources or charges. Authentication, one-time challenge checks and rate limits still apply.

Credential: a signed-in session.

Parameters

DELETE /v1/auth/sessions/{session_id} parameters
NameInTypeRequiredNotes
session_idpathstringyes

Responses

DELETE /v1/auth/sessions/{session_id} responses
StatusMeaning
204Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
404not_found: no such resource for this credential. A resource of another organization answers the same.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Example

curl https://api.faxforagents.com/v1/auth/sessions/SESSION_ID \
  -X DELETE

POST /v1/auth/switch-organization

Look at another organization you belong to

Idempotency-Key is accepted but ignored for this session-only authentication action. Repeated requests do not create customer resources or charges. Authentication, one-time challenge checks and rate limits still apply.

Credential: a signed-in session.

Request body application/json, required

POST /v1/auth/switch-organization request body
FieldTypeRequiredDescription
org_idstringyes

Responses

POST /v1/auth/switch-organization responses
StatusMeaning
200Successful Response
401No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused.
403The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required.
404not_found: no such resource for this credential. A resource of another organization answers the same.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

POST /v1/auth/switch-organization response fields
FieldTypeRequiredDescription
csrf_tokenstringyes
default_environmentstring (live, test)noThe environment the dashboard opens on: live once the organization has a funded wallet or a paid one-time order, else test.
organizationobject OrganizationSummary or nullyes
organizationsarray of object OrganizationSummaryyes
passkeysintegeryes
providersobject Providersyes
sessionobject SessionInfoyes
userobject UserOutyes

Example

curl https://api.faxforagents.com/v1/auth/switch-organization \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"org_id":"org_example"}'