Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Pricklymails Com Auth API
version: 0.9.0
contact:
email: support@pricklymails.com
license:
name: Proprietary
description: 'Operations tagged auth across 2 of this provider''s published API definitions: pricklymails-com-openapi.json, pricklymails-com-openapi.yaml. Each path carries the servers of the definition it was published in.'
tags:
- name: Auth
description: Registration, login, token refresh, password reset, and 2FA.
paths:
/api/auth/2fa/verify:
post:
tags:
- Auth
summary: Completes a sign-in that requires two-factor authentication
description: Send the `temp_token` from the login response together with a TOTP code or an unused backup code. On success the body carries an access token, the refresh token is set in an httpOnly `prickly_refresh` cookie, and a non-httpOnly `pm_session` cookie holds the session expiry as a hint (it is never a credential). Like the password login, it recognizes the browser by its `pm_device` cookie and sets one when the request carried none; spending a backup code emails the account owner.
operationId: verify_2fa
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Verify2FARequest'
required: true
responses:
'200':
description: 2FA verified; authenticated.
content:
application/json:
schema:
$ref: '#/components/schemas/AuthResponse'
'401':
description: Temp token invalid, or the TOTP/backup code is wrong.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
'429':
description: Too many failed 2FA attempts; temporarily locked.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
/api/auth/confirm:
post:
tags:
- Auth
summary: Confirms an account with the six-digit code mailed at signup, and signs it in
description: 'One code takes four guesses: a wrong code, an expired one, a burned one and an address with no account waiting for a code all answer the same `confirm_account.invalid_code`, after which a new code comes from `POST /api/auth/resend-confirmation`. With the `preview_token` of the check the visitor ran on this address before signing up, that check becomes the account''s first job, never billed, and `first_job_uuid` names it.'
operationId: confirm
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ConfirmAccountRequest'
required: true
responses:
'200':
description: Email address confirmed; authenticated.
content:
application/json:
schema:
$ref: '#/components/schemas/ConfirmAccountResponse'
'401':
description: The code does not confirm an account (`confirm_account.invalid_code`).
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
/api/auth/forgot-password:
post:
tags:
- Auth
summary: Requests a password reset email
description: The response is `204` whether or not the address has an account, so it cannot be used to discover registered addresses.
operationId: forgot_password
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ForgotPasswordRequest'
required: true
responses:
'204':
description: Reset email sent if the address is registered. Same response whether or not the account exists.
'400':
description: Malformed email address.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
/api/auth/login:
post:
tags:
- Auth
summary: Signs in with an email and a password
description: 'On success the body carries an access token, the refresh token is set in an httpOnly `prickly_refresh` cookie scoped to `/api/auth`, and a non-httpOnly `pm_session` cookie holds the session expiry as a hint for browser clients (it is never a credential). A browser the account has not signed in from before is recognized by an httpOnly `pm_device` cookie (scoped to `/api/auth`, valid 364 days) that this response sets when the request carried none; a sign-in from a device that is new to the account raises a security notice on the notifications page and by email, and never blocks the sign-in. When the account has two-factor authentication enabled, the response is instead a `{ requires_2fa, temp_token }` body with no cookie; complete the sign-in with `POST /api/auth/2fa/verify`.
A correct password on an account whose email is not confirmed yet returns `403` with the `authenticate_user.email_not_confirmed` code. Confirming the account (`POST /api/auth/confirm`) signs the user in directly.'
operationId: login
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
required: true
responses:
'200':
description: Authenticated, or a 2FA challenge requiring a follow-up call to `/api/auth/2fa/verify`.
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'401':
description: Invalid credentials, banned account, or deleted account.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
'403':
description: Correct password, but the account's email is not confirmed yet.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
'429':
description: Account temporarily locked after repeated failed attempts.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
/api/auth/logout:
post:
tags:
- Auth
summary: Signs out by revoking the refresh token in the `prickly_refresh` cookie
description: The response clears the `prickly_refresh` and `pm_session` cookies.
operationId: logout
responses:
'204':
description: Refresh token revoked (or none present); browser cookies cleared. Idempotent.
/api/auth/refresh:
post:
tags:
- Auth
summary: Exchanges the refresh token for a new access token
description: 'Reads the refresh token from the `prickly_refresh` httpOnly cookie and replies with a new access token in the body plus fresh `prickly_refresh` and `pm_session` cookies. Each refresh token is single-use: send back the cookie from the latest response.
A request without the `prickly_refresh` cookie means there is no session to restore and returns `200` with a `null` body. A cookie that is present but invalid, expired, or already used returns `401`.'
operationId: refresh
responses:
'200':
description: Rotated access token, with fresh refresh and session cookies. A `null` body means no `prickly_refresh` cookie was present; nothing to restore.
content:
application/json:
schema:
$ref: '#/components/schemas/AuthResponse'
'401':
description: Refresh token invalid, expired, or its user no longer exists.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
/api/auth/register:
post:
tags:
- Auth
summary: Starts account creation with an email and a password
description: 'The optional `locale` (`"en"` or `"fr"`, English when absent) sets the language of every email sent to the account, starting with the confirmation code. The response is `204` whether the address is new or already registered, so it cannot be used to discover registered addresses: a new address receives a six-digit confirmation code, an already-registered one receives an "account already exists" notice instead. Neither outcome signs the caller in: `POST /api/auth/confirm` with the code does.
The address is also checked for deliverability before the account is created: an undeliverable address is rejected with `register_user.undeliverable_email` and a disposable address with `register_user.disposable_email`. A `preview_token` from `POST /api/auth/signup-preview` for the same address stands in for that check.'
operationId: register
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/RegisterRequest'
required: true
responses:
'204':
description: Registration accepted. Same response whether the email was new or already registered.
'400':
description: Invalid email, invalid password, an unsupported locale (`register_user.invalid_locale`), a disposable email (`register_user.disposable_email`), or an undeliverable email (`register_user.undeliverable_email`).
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
/api/auth/resend-confirmation:
post:
tags:
- Auth
summary: Sends a fresh confirmation code for a signup whose code expired, was burned by…
description: The response is `204` whether or not the address has an account or is already confirmed, so it cannot be used to discover registered addresses. A request inside the cooldown that follows a code, or past the account's daily allowance of codes, sends nothing.
operationId: resend_confirmation
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ResendConfirmationRequest'
required: true
responses:
'204':
description: A new code sent if the address is registered, not yet confirmed, its last code is older than the resend cooldown, and it has not reached its daily allowance of codes. Same response in every other case.
'400':
description: Malformed email address.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
/api/auth/reset-password:
post:
tags:
- Auth
summary: Sets a new password using the token from the password reset email
description: Returns `204` on success.
operationId: reset_password
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ResetPasswordRequest'
required: true
responses:
'204':
description: Password changed.
'400':
description: New password fails the password policy.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
'401':
description: Reset token invalid, expired, or already used.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
'404':
description: No account matches the reset token.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
/api/auth/signup-preview:
post:
tags:
- Auth
summary: Checks the address a visitor is about to sign up with, through the pipeline…
description: 'The response says which checks passed, never the score, the verdict or the reasons: those wait on the server under `preview_token` until the account is confirmed, when `POST /api/auth/confirm` records them as its first job. `POST /api/auth/register` with the same token reuses the result rather than checking the address again. Previews and registrations share one per-IP budget.'
operationId: signup_preview
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/SignupPreviewRequest'
required: true
responses:
'200':
description: Which checks passed, and whether the address may sign up.
content:
application/json:
schema:
$ref: '#/components/schemas/SignupPreviewResponse'
'400':
description: Malformed email address (`signup_preview.invalid_email`).
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
'429':
description: Too many previews and registrations from this address.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
/api/auth/verify-email-change:
post:
tags:
- Auth
summary: Applies a pending email change with the token from the confirmation email sent…
description: Returns `204` on success.
operationId: verify_email_change
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/VerifyEmailChangeRequest'
required: true
responses:
'204':
description: Email address change applied.
'401':
description: Email-change token invalid or expired.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
'409':
description: The new email address was taken by another account in the meantime.
content:
application/json:
schema:
$ref: '#/components/schemas/ApiErrorBody'
components:
schemas:
UserResponse:
type: object
description: Public representation of a user.
required:
- uuid
- email
- role
- email_verified
- locale
- created_at
properties:
created_at:
type: string
description: Account creation timestamp (ISO 8601).
email:
type: string
description: Email address.
email_verified:
type: boolean
description: Whether the email has been verified.
locale:
type: string
description: Language of the account's emails (`"en"` or `"fr"`).
role:
type: string
description: User role (`"user"` or `"admin"`).
uuid:
type: string
description: Public UUID.
ApiErrorBody:
type: object
description: 'Error envelope returned with every non-success status: `{ "error": { "code", "message" } }`.'
required:
- error
properties:
error:
$ref: '#/components/schemas/ApiErrorDetail'
description: Error detail.
SmtpStatusDto:
type: string
description: Outcome of the SMTP probe.
enum:
- deliverable
- undeliverable
- unknown
- skipped
LoginResponse:
oneOf:
- $ref: '#/components/schemas/AuthResponse'
description: Standard authentication response (no 2FA required).
- type: object
description: 2FA is required; the client must call `/api/auth/2fa/verify` with the temp token.
required:
- requires_2fa
- temp_token
properties:
requires_2fa:
type: boolean
description: Always `true` when this variant is returned.
temp_token:
type: string
description: A short-lived token to send to `POST /api/auth/2fa/verify` with the code.
description: 'Response returned by the login endpoint.
The body is either a full authentication response or, when two-factor authentication is required, a
`{ requires_2fa, temp_token }` object.'
ResendConfirmationRequest:
type: object
description: Request body for `POST /api/auth/resend-confirmation`.
required:
- email
properties:
email:
type: string
description: The email address of the account requesting a fresh confirmation code.
ConfirmAccountRequest:
type: object
description: Request body for `POST /api/auth/confirm`.
required:
- email
- code
properties:
code:
type: string
description: The six-digit code from the confirmation email.
email:
type: string
description: The address the account was registered under.
preview_token:
type:
- string
- 'null'
description: 'The `preview_token` of the check the visitor ran on this address before signing up: that check becomes the
account''s first job.'
Verify2FARequest:
type: object
description: Request body for `POST /api/auth/2fa/verify`.
required:
- temp_token
- code
properties:
code:
type: string
description: The TOTP code or backup code.
temp_token:
type: string
description: The `temp_token` returned by login when 2FA is required.
AuthResponse:
type: object
description: 'Response body for authentication endpoints.
The refresh token is delivered in an httpOnly cookie, never in the JSON body.'
required:
- access_token
- expires_in
- user
properties:
access_token:
type: string
description: Access token (JWT), sent as a Bearer credential.
expires_in:
type: integer
format: int64
description: Access token lifetime in seconds.
minimum: 0
user:
$ref: '#/components/schemas/UserResponse'
description: Authenticated user details.
ResetPasswordRequest:
type: object
description: Request body for `POST /api/auth/reset-password`.
required:
- token
- new_password
properties:
new_password:
type: string
description: The new password, subject to the same rules as at registration.
token:
type: string
description: The reset token from the password reset email.
ForgotPasswordRequest:
type: object
description: Request body for `POST /api/auth/forgot-password`.
required:
- email
properties:
email:
type: string
description: The email address of the account requesting a password reset.
PreviewChecksDto:
type: object
description: 'Which checks passed for an address a visitor previews before signing up: the shape of the per-check result object,
without the mail exchanger hostnames.'
required:
- syntax
- mx
- disposable
- role
- free
- typo
- smtp
- catchall
properties:
catchall:
$ref: '#/components/schemas/ProviderFlagDto'
description: Whether the domain is a catch-all server.
disposable:
$ref: '#/components/schemas/ProviderFlagDto'
description: Whether the domain is from a known disposable provider.
free:
$ref: '#/components/schemas/ProviderFlagDto'
description: Whether the domain is a known free webmail provider.
mx:
$ref: '#/components/schemas/MxPreviewDto'
description: Whether the domain publishes a usable mail exchanger.
role:
$ref: '#/components/schemas/ProviderFlagDto'
description: Whether the local-part is a known role-based address.
smtp:
$ref: '#/components/schemas/SmtpCheckDto'
description: SMTP deliverability probe result.
syntax:
type: boolean
description: Whether the email syntax is valid.
typo:
$ref: '#/components/schemas/TypoCheckDto'
description: Typo detection result, with the corrected address when one was found.
SignupPreviewResponse:
type: object
description: 'Response body for `POST /api/auth/signup-preview`.
Says which checks passed and nothing more: the score, the verdict and the reasons stay on the server until the
account the visitor creates is confirmed.'
required:
- signup
properties:
checks:
oneOf:
- type: 'null'
- $ref: '#/components/schemas/PreviewChecksDto'
description: Which checks passed, absent when no check ran.
preview_token:
type:
- string
- 'null'
description: 'The handle on the stored result, to send back with `POST /api/auth/register` and `POST /api/auth/confirm`.
Present only when `signup` is `allowed`.'
signup:
$ref: '#/components/schemas/SignupEligibilityDto'
description: Whether the address may sign up.
RegisterRequest:
type: object
description: Request body for `POST /api/auth/register`.
required:
- email
- password
properties:
email:
type: string
description: The email address for the new account.
locale:
type:
- string
- 'null'
description: 'The language of the page the user signs up on (`"en"` or `"fr"`), which every email to the account then uses.
English when absent.'
password:
type: string
description: 'The password for the new account: 12 to 128 characters with an uppercase letter, a digit, and a special
character.'
preview_token:
type:
- string
- 'null'
description: 'The `preview_token` that `POST /api/auth/signup-preview` returned for this same address: its result stands in
for the check registration would otherwise run again. Ignored when unknown, expired, or issued for another
address.'
LoginRequest:
type: object
description: Request body for `POST /api/auth/login`.
required:
- email
- password
properties:
email:
type: string
description: The email address of the account.
password:
type: string
description: The account password.
SignupPreviewRequest:
type: object
description: Request body for `POST /api/auth/signup-preview`.
required:
- email
properties:
email:
type: string
description: The address the visitor is about to sign up with.
VerifyEmailChangeRequest:
type: object
description: Request body for `POST /api/auth/verify-email-change`.
required:
- token
properties:
token:
type: string
description: The confirmation token from the link in the email-change email.
SmtpCheckDto:
type: object
description: SMTP deliverability probe result.
required:
- status
properties:
status:
$ref: '#/components/schemas/SmtpStatusDto'
description: 'SMTP probe outcome: `deliverable` (the server accepted the `RCPT TO` command), `undeliverable` (the server
permanently rejected the recipient, 5xx), `unknown` (no definitive answer: a temporary refusal, a timeout, a
connection failure, or a server that refused the probe itself), or `skipped` (not requested, or not
needed because the answer was already known).'
ApiErrorDetail:
type: object
description: Detail of an error response.
required:
- code
properties:
code:
type: string
description: Stable key identifying the error (for example `"verify.invalid_uuid"`). Clients localize by this key.
message:
type:
- string
- 'null'
description: 'Present on most error kinds and equal to `code`; the API returns no localized text.
Absent for error kinds that carry no extra detail.'
ProviderFlagDto:
oneOf:
- type: boolean
description: The check ran; carries the real boolean result.
- type: object
description: The check was not run.
required:
- status
properties:
status:
type: string
description: Always `"skipped"`.
description: 'A boolean provider or risk-flag check result (`disposable`, `role`, `free`, `catchall`).
Renders as a bare `true`/`false` when the check ran, or as `{ "status": "skipped" }` when it did not run (excluded
from the caller''s check set), the same skipped shape the `mx` and `smtp` checks use, so an unrun check never reads
as a definitive negative result.'
ConfirmAccountResponse:
allOf:
- $ref: '#/components/schemas/AuthResponse'
description: The session, as every sign-in returns it.
- type: object
properties:
first_job_uuid:
type:
- string
- 'null'
format: uuid
description: 'The job recording the check the visitor ran on their address before signing up, readable through
`GET /api/jobs/{uuid}`. Present only when `preview_token` named one.'
description: 'Response body for `POST /api/auth/confirm`: the session of the confirmed account.'
SignupEligibilityDto:
type: string
description: Whether the previewed address may sign up.
enum:
- allowed
- undeliverable
- disposable
- unchecked
MxPreviewDto:
oneOf:
- type: object
description: The check ran.
required:
- valid
properties:
valid:
type: boolean
description: Whether the domain publishes at least one usable mail exchanger.
- type: object
description: The check was not run.
required:
- status
properties:
status:
type: string
description: Always `"skipped"`.
description: The MX check of a signup preview.
TypoCheckDto:
type: object
description: Typo detection result.
required:
- detected
properties:
detected:
type: boolean
description: Whether a domain typo was detected.
suggested_domain:
type:
- string
- 'null'
description: 'Corrected domain only (e.g. `"gmail.com"`), present on `GET /api/verify/{uuid}` when a typo was detected.
Only the domain is returned (zero-retention policy); the caller already has the local part to combine it with.'
suggestion:
type:
- string
- 'null'
description: 'Full corrected address (e.g. `"user@gmail.com"`), only ever present on the synchronous submit response where the
raw email is available in-request.
Always absent on `GET /api/verify/{uuid}`, because the submitted address is never stored (zero-retention
policy); `suggested_domain` is that path''s equivalent.'
securitySchemes:
bearer_api_key:
type: http
scheme: bearer
description: A `pm_live_*` or `pm_test_*` API key.
jwt:
type: http
scheme: bearer
description: A short-lived token from `/api/auth/login`, or `/oauth/token` for an agent.
x-refined-from:
- pricklymails-com-openapi.json
- pricklymails-com-openapi.yaml