Skip to content

Sign in / sign up with a Sign in with Apple credential

POST
/auth/apple
curl --request POST \
--url https://api.tovarifinancial.com/auth/apple \
--header 'Content-Type: application/json' \
--data '{ "identityToken": "example", "authorizationCode": "example", "nonce": "example", "fullName": { "givenName": "example", "familyName": "example" } }'

The Apple bridge, the counterpart of POST /auth/google (KAN-3109 documents it; the handler is src/handlers/auth/apple-auth.ts). Identity resolves by the Apple sub first and the verified email second, because Apple sends the email on the FIRST authorization only. When neither resolves and the email is a Hide-My-Email private-relay address, NOTHING is provisioned: the 200 carries { confirmationRequired, confirmationToken, expiresIn } instead (KAN-2823), and only a second POST carrying { confirmationToken } — that key alone, within expiresIn seconds — creates the account (201). A non-relay address with no match provisions immediately (201). X-Idempotency-Key is accepted and logged but NOT persisted, exactly as on POST /auth/google: the new-user path mints a fresh org id per request, so a stored replay could never be found again; duplicate deliveries converge structurally instead (one Apple binding per sub, one Cognito user per address) and the loser answers 409. Also reachable, and deliberately not published as a response code because this operation sits outside the tenant gate: 403 SIGNUP_RESTRICTED when a deployment restricts sign-ups to an allowlist and this NEW account is not on it — an existing account signs in regardless. The 503 is SERVICE_UNAVAILABLE for an Apple, Cognito or key-service outage and MISCONFIGURED when a deployment value the request needs is missing — a code no retry can clear until the deployment is fixed.

Media typeapplication/json
One of:

Identity variant — an Apple credential from the native or web Sign in with Apple ceremony.

object
identityToken
required

The Sign in with Apple identity token (JWT).

string
>= 1 characters
authorizationCode
required

The single-use Sign in with Apple authorization code.

string
>= 1 characters
nonce

The raw nonce the client supplied to the Apple ceremony. Optional; when the token carries a nonce claim it must correspond to this value.

string
fullName

Apple sends the name on the FIRST authorization only. Untrusted: used solely to name a brand-new account, never to change an existing one.

object
givenName
string
familyName
string
Examplegenerated
{
"identityToken": "example",
"authorizationCode": "example",
"nonce": "example",
"fullName": {
"givenName": "example",
"familyName": "example"
}
}

Existing user authenticated (tokens issued), OR a second factor is required (challenge returned, no tokens), OR a Hide-My-Email sign-up needs the user to confirm (confirmation token returned, nothing written)

Media typeapplication/json
object
data
required
One of:

Existing user authenticated — tokens issued.

object
accessToken
required
string
idToken
required
string
refreshToken
required
string
expiresIn
required

Access/ID token lifetime in seconds.

integer
user
required
object
id
required
string format: uuid
email
required
string
name
required
string
nullable
orgId
required
string format: uuid
orgRole
required
string
debugConsoleEnabled
required
boolean
onboardingCompleted
required
boolean
outstandingPolicies
required
Array<object>
object
id
required
string format: uuid
policyType
required
string
version
required
integer
title
required
string
firstAcceptance
required

TRUE when the outstanding policies are a first acceptance rather than an update.

boolean
verification
required
object
status
required

Derived from users.email_verified, the sole source of truth for the gate.

string
Allowed values: verified unverified
codeDeliveryDetails
object
destination
required
string
deliveryMedium
required
string
cooldownSeconds

Seconds the resend button stays disabled after an interactive send.

integer
sendsRemaining
integer
language
required

The session user display-language state (KAN-3773).

object
spanishEnabled
required

The per-environment Spanish rollout flag. False ⇒ effectiveLocale is always ‘en’.

boolean
preference
required

The explicit choice; null = use device language.

string
nullable
Allowed values: en es
detected
required

This request’s X-Client-Locale, else the last-seen device language, else null.

string
nullable
Allowed values: en es
effectiveLocale
required

The language to display: ‘en’ when the flag is off, else preference ?? detected ?? ‘en’.

string
Allowed values: en es
isNewUser
required

Always false on a 200 — a brand-new account answers 201.

boolean
passwordSignInReset
required

TRUE when this request replaced the account password to complete the Apple link.

boolean
message
string
requestId
required
string
timestamp
required
string format: date-time
Example
{
"data": {
"verification": {
"status": "verified"
},
"language": {
"preference": "en",
"detected": "en",
"effectiveLocale": "en"
}
}
}

New account created from the Apple credential (or from a confirmed Hide-My-Email sign-up); tokens issued

Media typeapplication/json
object
data
required
object
accessToken
required
string
idToken
required
string
refreshToken
required
string
expiresIn
required

Access/ID token lifetime in seconds.

integer
user
required
object
id
required
string format: uuid
email
required
string
name
required
string
nullable
orgId
required
string format: uuid
orgRole
required
string
debugConsoleEnabled
required
boolean
onboardingCompleted
required
boolean
outstandingPolicies
required
Array<object>
object
id
required
string format: uuid
policyType
required
string
version
required
integer
title
required
string
firstAcceptance
required

TRUE when the outstanding policies are a first acceptance rather than an update.

boolean
verification
required
object
status
required

Derived from users.email_verified, the sole source of truth for the gate.

string
Allowed values: verified unverified
codeDeliveryDetails
object
destination
required
string
deliveryMedium
required
string
cooldownSeconds

Seconds the resend button stays disabled after an interactive send.

integer
sendsRemaining
integer
language
required

The session user display-language state (KAN-3773).

object
spanishEnabled
required

The per-environment Spanish rollout flag. False ⇒ effectiveLocale is always ‘en’.

boolean
preference
required

The explicit choice; null = use device language.

string
nullable
Allowed values: en es
detected
required

This request’s X-Client-Locale, else the last-seen device language, else null.

string
nullable
Allowed values: en es
effectiveLocale
required

The language to display: ‘en’ when the flag is off, else preference ?? detected ?? ‘en’.

string
Allowed values: en es
isNewUser
required

Always true on a 201.

boolean
message
string
requestId
required
string
timestamp
required
string format: date-time
Example
{
"data": {
"verification": {
"status": "verified"
},
"language": {
"preference": "en",
"detected": "en",
"effectiveLocale": "en"
}
}
}

VALIDATION_ERROR — no body, a body that is not a JSON object, a missing identityToken or authorizationCode (named in fieldErrors), a confirm body that is not exactly one non-empty string confirmationToken, or an Apple email that is not a valid address. A Cognito rejection of the new account’s creation parameters also answers 400 with its own code.

Media typeapplication/json
object
errorCode
required

Machine-readable error code (AUTH_ERROR_CODES).

string
Allowed values: INVALID_EMAIL_FORMAT USER_ALREADY_EXISTS INVALID_INVITE_CODE WRONG_PASSWORD USER_NOT_FOUND AUTHENTICATION_FAILED EXPIRED_CODE INVALID_CODE INVALID_PASSWORD VALIDATION_ERROR SIGNUP_FAILED DATABASE_ERROR SERVICE_UNAVAILABLE PLAID_ERROR IDEMPOTENCY_CONFLICT ACCOUNT_ALREADY_LINKED NOT_FOUND
message
required
string
fieldErrors

Optional field-level validation errors, keyed by field name.

object
key
additional properties
Array<string>
requestId
required
string
timestamp
required
string format: date-time
Example
{
"errorCode": "INVALID_EMAIL_FORMAT"
}

AUTHENTICATION_FAILED — the Apple credential (or confirmation token) was rejected, carries no usable identity, or resolves to no active account; one fixed message whichever check failed. APPLE_CONFIRMATION_EXPIRED — a validly signed confirmation token that has expired, or whose sign-up already completed or now matches an existing account; one fixed message for all three, and the client starts again from email and password. An identity-provider refusal while creating a new account can also surface on this status with that provider’s mapped code.

Media typeapplication/json
object
errorCode
required

Machine-readable error code (AUTH_ERROR_CODES).

string
Allowed values: INVALID_EMAIL_FORMAT USER_ALREADY_EXISTS INVALID_INVITE_CODE WRONG_PASSWORD USER_NOT_FOUND AUTHENTICATION_FAILED EXPIRED_CODE INVALID_CODE INVALID_PASSWORD VALIDATION_ERROR SIGNUP_FAILED DATABASE_ERROR SERVICE_UNAVAILABLE PLAID_ERROR IDEMPOTENCY_CONFLICT ACCOUNT_ALREADY_LINKED NOT_FOUND
message
required
string
fieldErrors

Optional field-level validation errors, keyed by field name.

object
key
additional properties
Array<string>
requestId
required
string
timestamp
required
string format: date-time
Example
{
"errorCode": "INVALID_EMAIL_FORMAT"
}

Conflict — errorCode is one of: IDENTITY_CONFLICT (the verified email belongs to an account already bound to a different Apple ID); USER_ALREADY_EXISTS (an account exists for this email that this sign-in cannot create, link or heal, including a concurrent sign-up of the same Apple ID that won the race); UNCLAIMED_INVITE_ACCOUNT (the email resolves to an account created by accepting a household invite that has never verified its email — a fixed-message refusal, carrying no details, that writes nothing and tells the user to sign in with the password set when the invitation was accepted and verify their email first).

Media typeapplication/json
object
errorCode
required

Machine-readable error code (AUTH_ERROR_CODES).

string
Allowed values: INVALID_EMAIL_FORMAT USER_ALREADY_EXISTS INVALID_INVITE_CODE WRONG_PASSWORD USER_NOT_FOUND AUTHENTICATION_FAILED EXPIRED_CODE INVALID_CODE INVALID_PASSWORD VALIDATION_ERROR SIGNUP_FAILED DATABASE_ERROR SERVICE_UNAVAILABLE PLAID_ERROR IDEMPOTENCY_CONFLICT ACCOUNT_ALREADY_LINKED NOT_FOUND
message
required
string
fieldErrors

Optional field-level validation errors, keyed by field name.

object
key
additional properties
Array<string>
requestId
required
string
timestamp
required
string format: date-time
Example
{
"errorCode": "INVALID_EMAIL_FORMAT"
}

TOO_MANY_REQUESTS — the identity provider is shedding load while creating a new account. Retry after a short back-off.

Media typeapplication/json
object
errorCode
required

Machine-readable error code (AUTH_ERROR_CODES).

string
Allowed values: INVALID_EMAIL_FORMAT USER_ALREADY_EXISTS INVALID_INVITE_CODE WRONG_PASSWORD USER_NOT_FOUND AUTHENTICATION_FAILED EXPIRED_CODE INVALID_CODE INVALID_PASSWORD VALIDATION_ERROR SIGNUP_FAILED DATABASE_ERROR SERVICE_UNAVAILABLE PLAID_ERROR IDEMPOTENCY_CONFLICT ACCOUNT_ALREADY_LINKED NOT_FOUND
message
required
string
fieldErrors

Optional field-level validation errors, keyed by field name.

object
key
additional properties
Array<string>
requestId
required
string
timestamp
required
string format: date-time
Example
{
"errorCode": "INVALID_EMAIL_FORMAT"
}

Internal error (no internal detail leaked)

Media typeapplication/json
object
errorCode
required

Machine-readable error code (AUTH_ERROR_CODES).

string
Allowed values: INVALID_EMAIL_FORMAT USER_ALREADY_EXISTS INVALID_INVITE_CODE WRONG_PASSWORD USER_NOT_FOUND AUTHENTICATION_FAILED EXPIRED_CODE INVALID_CODE INVALID_PASSWORD VALIDATION_ERROR SIGNUP_FAILED DATABASE_ERROR SERVICE_UNAVAILABLE PLAID_ERROR IDEMPOTENCY_CONFLICT ACCOUNT_ALREADY_LINKED NOT_FOUND
message
required
string
fieldErrors

Optional field-level validation errors, keyed by field name.

object
key
additional properties
Array<string>
requestId
required
string
timestamp
required
string format: date-time
Example
{
"errorCode": "INVALID_EMAIL_FORMAT"
}

Service temporarily unavailable — errorCode is SERVICE_UNAVAILABLE. A TRANSIENT, RETRYABLE condition rather than a defect in the request: a query that exceeded its time budget, a lost or refused database connection, a saturated connection pool, or an outage at an upstream provider the request depends on (the authentication provider, or the bank data provider on a bank-connection operation). The identical request may succeed on retry — back off briefly and, on a write, resend the same X-Idempotency-Key where the operation accepts one.

Media typeapplication/json
object
errorCode
required

Machine-readable error code (AUTH_ERROR_CODES).

string
Allowed values: INVALID_EMAIL_FORMAT USER_ALREADY_EXISTS INVALID_INVITE_CODE WRONG_PASSWORD USER_NOT_FOUND AUTHENTICATION_FAILED EXPIRED_CODE INVALID_CODE INVALID_PASSWORD VALIDATION_ERROR SIGNUP_FAILED DATABASE_ERROR SERVICE_UNAVAILABLE PLAID_ERROR IDEMPOTENCY_CONFLICT ACCOUNT_ALREADY_LINKED NOT_FOUND
message
required
string
fieldErrors

Optional field-level validation errors, keyed by field name.

object
key
additional properties
Array<string>
requestId
required
string
timestamp
required
string format: date-time
Example
{
"errorCode": "INVALID_EMAIL_FORMAT"
}