Authentication API

Sign-in itself: register, log in, log out, Google, magic links, password reset, email verification. These set and clear a session cookie, live on the API root rather than under `/api/v1`, and are not part of the versioned contract.

Updated

On this page

20 operations, generated from openapi/signclad.v1.yaml. Try them in the interactive reference, browse every endpoint group, or read the guides for the prose version.

POST /register

Create an account Extension

Creates a user, their account, and their first workspace, and signs them in. Session cookie, not a token: call GET /sanctum/csrf-cookie first and send the X-XSRF-TOKEN header, as with every route on the API root. A six-digit, ten-minute verification code is emailed immediately; until it is accepted, the session can only call GET /api/v1/me and the verification routes. 6 requests per minute per IP. 403 when registration is closed, 409 when the caller is already signed in.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Request body

application/json

Property Type Required Description
email string (email) Required Lower-cased and trimmed before it is stored.
name string Required
password string Required At least 12 characters and at most 72 bytes, checked against a breach corpus. A long password that appears in a known breach is refused with a 422, so length alone is not a guarantee the value will be accepted.
workspace_name string Required
locale string Optional
timezone string Optional

Responses

Status Description
201 Registered and signed in.
403 Authenticated, but not allowed to do this: an insufficient role, a credential type the route refuses, an unenrolled second factor, or a signer acting out of turn. meta.error always says which.…
409 Already signed in. This status is not in the shared renderer, so the body is Laravel's default {"message": "You are already signed in."} rather than the usual envelope.
422 errors.email reads An account with this email already exists. Sign in instead. for a taken address — including when two registrations race — or This workspace requires single sign-on.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X POST https://api.signclad.com/register \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /login

Sign in Extension

Signs in with an email and password, 6 attempts per minute per IP. Two different 200 bodies. When no second factor is needed the response is the session payload. When one is, the response is a challenge — mfa_required: true and the methods to offer — and it is still a 200, not a 4xx, because nothing went wrong. The two shapes share no discriminating property; branch on mfa_required.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Request body

application/json

Property Type Required Description
email string (email) Required
password string Required
remember boolean Optional

Responses

Status Description
200 Signed in, or challenged for a second factor.
422 errors.email reads These credentials do not match our records. — the same message whether the address is unknown or the password is wrong — or This workspace requires single sign-on.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X POST https://api.signclad.com/login \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

Example response (200)

{
  "ok": true,
  "user": {
    "id": "7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d",
    "name": "Bryce Matheson",
    "email": "[email protected]",
    "email_verified": true
  },
  "mfa": {
    "required": true,
    "enrolled": true,
    "methods": [
      "passkey",
      "totp",
      "recovery"
    ],
    "enforced_from": "2026-10-01T00:00:00Z",
    "days_remaining": null,
    "blocked": false
  }
}

POST /login/mfa

Answer a second-factor challenge Extension

Completes the challenge POST /login (or a magic link) started. The pending challenge lives in the session, so nothing identifies it in the body. 10 attempts per minute per IP.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Request body

application/json

Property Type Required Description
method string Required
code string or null Optional
credential object Optional The credential the browser returned, forwarded verbatim.

Responses

Status Description
200 Signed in.
422 errors.method reads The sign-in attempt expired. Start again. when no challenge is pending. errors.code reads That code is not valid., `The sign-in attempt expired.…
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X POST https://api.signclad.com/login/mfa \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /logout

Sign out Extension

Clears this session. Always 200, even when nobody was signed in.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Responses

Status Description
200 Signed out.

Example request

curl -X POST https://api.signclad.com/logout \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /logout/everywhere

Sign out of every session Extension

Revokes every session for this user, this one included. The count of revoked sessions goes to the security event log, not to the response.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Responses

Status Description
200 Signed out everywhere.

Example request

curl -X POST https://api.signclad.com/logout/everywhere \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /auth/magic_link

Request a sign-in link Extension

Emails a single-use sign-in link. The response is identical whether or not the address has an account — that is the point — so it never confirms who is registered. 5 per minute per IP.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Request body

application/json

Property Type Required Description
email string (email) Required

Responses

Status Description
200 Sent, if there was somewhere to send it.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X POST https://api.signclad.com/auth/magic_link \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /auth/magic_link/consume

Sign in with a link Extension

Exchanges the token from a sign-in link for a session. Like POST /login this answers either the session payload or a second-factor challenge, both as 200. The session payload here carries no mfa block and reports email_verified: true — following the link is itself proof of the address.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Request body

application/json

Property Type Required Description
token string Required

Responses

Status Description
200 Signed in, or challenged for a second factor.
422 errors.token reads That sign-in link is no longer valid. Ask for a new one.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X POST https://api.signclad.com/auth/magic_link/consume \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /auth/password/forgot

Request a password reset Extension

The same silence as the magic link — {"sent": true} whoever asked. 5 per minute per IP.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Request body

application/json

Property Type Required Description
email string (email) Required

Responses

Status Description
200 Sent, if there was somewhere to send it.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X POST https://api.signclad.com/auth/password/forgot \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /auth/password/reset

Set a new password from a reset link Extension

Sets the password and revokes every existing session, returning how many. It does not sign the caller in; they log in with the new password.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Request body

application/json

Property Type Required Description
password string Required At least 12 characters and at most 72 bytes, checked against a breach corpus. A long password that appears in a known breach is refused with a 422, so length alone is not a guarantee the value will be accepted.
token string Required

Responses

Status Description
200 Password changed.
422 errors.token reads That reset link is no longer valid. Ask for a new one.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X POST https://api.signclad.com/auth/password/reset \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /auth/email/verify

Verify a new account's email address Extension

Accepts the six-digit code sent after password registration. The code expires after ten minutes, works once, is bound to the signed-in browser session and current email address, and is retired after five failed guesses.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Request body

application/json

Property Type Required Description
code string Required

Responses

Status Description
204 Email verified, or it was already verified.
401 Missing or invalid credentials. Unauthenticated requests are rate limited at 50 per minute.…
422 The code is malformed, invalid, expired, or exhausted.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X POST https://api.signclad.com/auth/email/verify \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /auth/email/verify/resend

Send a fresh verification code Extension

Replaces the current code and sends a fresh one to the signed-in user's address. Always 204 when the address is already verified or nobody is signed in. Limited to one send per minute and five per hour for the account, plus 3 requests per minute per IP.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Responses

Status Description
204 Nothing to report, by design.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X POST https://api.signclad.com/auth/email/verify/resend \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

GET /auth/email/verify/{id}/{hash}

Verify an email address Extension

The link in the verification email. It is a Laravel signed URL — a tampered one is 403 before the controller runs — and it always redirects to the web app rather than answering JSON, with ?status=verified, already_verified, or invalid.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Path parameters

Name Type Description
id string The user's id.
hash string SHA-1 of the address being verified.

Responses

Status Description
302 Redirect to the web app's /auth/email-verified page with a status query parameter.
403 Authenticated, but not allowed to do this: an insufficient role, a credential type the route refuses, an unenrolled second factor, or a signer acting out of turn. meta.error always says which.…
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X GET https://api.signclad.com/auth/email/verify/{id}/{hash} \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

GET /auth/google/redirect

Start Google sign-in Extension

Redirects to Google with the openid profile email scopes. 20 per minute per IP.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Responses

Status Description
302 Redirect to Google.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X GET https://api.signclad.com/auth/google/redirect \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

GET /auth/google/callback

Finish Google sign-in Extension

Google's redirect target. Always redirects to the web app, never JSON: on success to the configured landing path, on failure to the login page with ?error=google_failed, google_no_email, or google_link_requires_verified_email.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Responses

Status Description
302 Redirect to the web app.

Example request

curl -X GET https://api.signclad.com/auth/google/callback \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /contact

Send a message to Signclad Extension

The marketing site's contact form. Session-backed and CSRF-protected like the rest of the root.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Request body

application/json

Property Type Required Description
email string (email) Required
message string Required
name string Required No line breaks — a header-injection guard.
subject string Required No line breaks.

Responses

Status Description
200 Sent.
422 Validation failed. errors maps a field name to a message string, or to an object keyed by an error code or by a per-item key. Values are never arrays.

Example request

curl -X POST https://api.signclad.com/contact \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

GET /sso/{workspace_slug}/login

Begin SSO sign-in Extension

Redirects to the IdP — a SAML AuthnRequest, or the OIDC authorization endpoint. Nothing here is authenticated; that is the point. There are no error bodies on this flow. Every failure redirects to the web app's login page with ?sso_error=<reason>, and the reasons are not_configured, not_available_on_plan, invalid_assertion, assertion_replayed, unknown_state, domain_not_allowed, no_email, no_membership, provider_unreachable, invalid_id_token, and provider_refused. An unknown slug, a plan without SSO, a missing connection, and an unusable one all answer the same 404, so nothing here reveals which workspaces exist or how they are configured.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Path parameters

Name Type Description
workspace_slug string The workspace's slug, as it appears in its SSO URLs.

Query parameters

Name Type Required Description
return_to string Optional Where to land after signing in. Must be a site-relative path; anything else — including a protocol-relative //host — is dropped silently rather than refused.

Responses

Status Description
302 To the IdP, or to the login page with an sso_error.
404 No such record for this workspace. Signclad never answers 404 for a record that exists: a 404 is authoritative.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X GET https://api.signclad.com/sso/{workspace_slug}/login \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /sso/{workspace_slug}/acs

SAML assertion consumer Extension

Where the IdP posts its SAMLResponse. The XML signature is the credential, so there is no session, no bearer token, and no CSRF token on this route. Five things are checked: the signature, the audience, InResponseTo, replay, and whether the asserted address is in an allowed domain. Success sets the session cookie and redirects into the app; every failure redirects to the login page with ?sso_error=<reason>.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Path parameters

Name Type Description
workspace_slug string The workspace's slug, as it appears in its SSO URLs.

Request body

application/x-www-form-urlencoded

Property Type Required Description
SAMLResponse string Required
RelayState string Optional Honoured only on an IdP-initiated sign-in.

Responses

Status Description
302 Into the app on success, or to the login page with an sso_error.
404 No such record for this workspace. Signclad never answers 404 for a record that exists: a 404 is authoritative.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X POST https://api.signclad.com/sso/{workspace_slug}/acs \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

GET /sso/{workspace_slug}/callback

OIDC redirect target Extension

Where the OIDC provider sends the browser back. The stored state row, the PKCE verifier, and the nonce are what bind the response to the request. Same redirect-only error model as the SAML routes.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Path parameters

Name Type Description
workspace_slug string The workspace's slug, as it appears in its SSO URLs.

Query parameters

Name Type Required Description
code string Optional
state string Optional
error string Optional Set by the provider when the person declined or it refused.

Responses

Status Description
302 Into the app on success, or to the login page with an sso_error.
404 No such record for this workspace. Signclad never answers 404 for a record that exists: a 404 is authoritative.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X GET https://api.signclad.com/sso/{workspace_slug}/callback \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

POST /sso/{workspace_slug}/slo

Sign out of SSO Extension

Clears the Signclad session and hands back the IdP's logout URL for the browser to visit, when the connection publishes one. Answers 200 with the same body whether or not there was a session to clear. The one SSO route that returns JSON rather than a redirect — and, unlike the assertion consumer, it is CSRF-protected, so a SPA must send the token. It does not accept an IdP-initiated LogoutRequest; there is no GET.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Path parameters

Name Type Description
workspace_slug string The workspace's slug, as it appears in its SSO URLs.

Responses

Status Description
200 Signed out. idp_logout_url is null when the IdP publishes none.
404 No such record for this workspace. Signclad never answers 404 for a record that exists: a 404 is authoritative.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X POST https://api.signclad.com/sso/{workspace_slug}/slo \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"

GET /sso/{workspace_slug}/metadata

Public SAML SP metadata Extension

The same service-provider metadata as GET /sso/metadata, at a public URL an IdP can fetch for itself. Cached five minutes. Every failure — unknown slug, unentitled plan, no connection, metadata that will not validate — is one undifferentiated 404.

Base URL
https://api.signclad.com
Authentication
None — this operation is public

Path parameters

Name Type Description
workspace_slug string The workspace's slug, as it appears in its SSO URLs.

Responses

Status Description
200 The SP metadata.
404 No such record for this workspace. Signclad never answers 404 for a record that exists: a 404 is authoritative.
429 Rate limited. The message contains the literal 429 Too Many Requests for compatibility with clients that match on it.…

Example request

curl -X GET https://api.signclad.com/sso/{workspace_slug}/metadata \
  -H "X-Api-Key: $SIGNCLAD_API_KEY"