---
title: "Verify a sign-in code"
method: POST
path: "/hd/desks/{desk_id}/auth/pin/verify"
tags: ["hd"]
---

# Verify a sign-in code

`POST /hd/desks/{desk_id}/auth/pin/verify`

## Request

- Base URL: `https://api.sparkvault.com/v1`
- URL: `https://api.sparkvault.com/v1/hd/desks/{desk_id}/auth/pin/verify`
- Auth: none required
- TypeScript: <https://skmtc.dev/sparkvault/apis/sparkvault-api/docs/hd/desks/:desk_id/auth/pin/verify/post.ts> (a function with typed inputs; its response is checked against the schema)

Trade the emailed code, with the challenge it answers and the binding secret the start answered, for the customer's session. Every attempt is counted before the code is compared, and a challenge opens at most one session. Every refusal of the code is the same 400 PIN_INVALID: a wrong, spent, expired or another desk's code, a missing binding secret, a page that did not start the sign-in, and a desk whose customers sign in with Secure Identity.

## Path parameters

- `desk_id` string, required

## Headers

- `X-SV-Client` 'help-desk-portal', required

## Request body

- object
  - `challenge_id` string, required
  - `binding_secret` string, required
  - `code` string, required: The six digits the email carried.

## Response `200`

The customer's session and record.

- object
  - `data` HelpDeskPinSession, required
    - `token` string, required: The help desk session token (`hdt_…`), the bearer of every signed-in request.
    - `expires_at` integer, required: Epoch seconds the session ends at the latest; it ends earlier once unused for 30 days.
    - `customer` HelpDeskCustomer, required: The signed-in customer, as the portal shows them.
      - `requester_id` string, required
      - `email` string, required: The address the customer proved.
      - `display_name` string, nullable, required: The name the desk shows the customer by; null until they give one.
      - `assurance` 'identity' | 'email_pin', required: How the customer last proved their address: Secure Identity, or an emailed code.
      - `email_updates` boolean: Whether the desk emails the customer updates on their requests. Present only where that choice is the desk's own switch, for a customer who last proved their address with an emailed code (`assurance` `email_pin`); a Secure Identity customer sets it in their notification preferences on auth.sv instead.
      - `needs_display_name` boolean, required: True until the customer gives a name: the portal asks for one first.
  - `meta` ResponseMeta: Envelope metadata present on every successful response.
    - `api_version` string
    - `request_id` string
    - `response_ms` integer
    - `timestamp` integer: Unix seconds.
    - `pools` object: Storage and bandwidth capacity snapshot for the account.
    - `billing` object
      - `past_due` boolean
    - `quota` object: This minute of the account request budget. Present on authenticated success responses that passed the throttle; absent otherwise.
      - `limit` integer
      - `used` integer
      - `remaining` integer
      - `resets_at` integer: Unix seconds.

## Other responses

- `400`: PIN_INVALID — the code is wrong or has expired. VALIDATION_ERROR — the body is no JSON object. CLIENT_HEADER_REQUIRED — see above.
- `409`: CONFLICT — the customer's record kept changing while the sign-in was recorded; try the code again.
- `429`: RATE_LIMIT_EXCEEDED — this address has spent its code attempts for the hour or the day, this client address its reads at the desk for the minute, or the sign-in would make the customer's record past the new customers the desk takes in an hour. `Retry-After` carries the seconds until the budget reopens, and `details` carries `limit` and `resets_at`.
- `503`: HELP_DESK_UNAVAILABLE — the desk does not take customers right now, or this customer is refused. BUDGET_UNAVAILABLE — a budget the request spends could not be counted, so the request was refused rather than let through uncounted; `Retry-After` carries the seconds to wait before trying again.

## Changes

- **2026-10-28** `a9a10e328ba5`: 16 info
  - the response property `error/details` became required for the status `400`
  - the response property `error/details` became required for the status `409`
  - the response property `error/details` became required for the status `429`
  - the response property `error/details` became required for the status `503`
  - …12 more
- **2026-10-28** `56d7c265a77a`: 4 breaking
  - the response property `error/details` became nullable for the status `400`
  - the response property `error/details` became nullable for the status `409`
  - the response property `error/details` became nullable for the status `429`
  - the response property `error/details` became nullable for the status `503`
- **2026-10-23** `ad4c62db5e08`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/sparkvault/apis/sparkvault-api/changes/hd/desks/:desk_id/auth/pin/verify/post.md)

---

[API](https://skmtc.dev/sparkvault/apis/sparkvault-api.md) - [All operations](https://skmtc.dev/sparkvault/apis/sparkvault-api/llms.txt) - [OpenAPI document](https://skmtc.dev/sparkvault/apis/sparkvault-api/revisions/97cfadccd0c0?raw)
