> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cloudhumans.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sign in and get a token

> Authenticate with email and password and receive the token used by every other API. One token covers Claudia, Cloud Chat and the services that follow — the account you see is derived from it, and the same token is valid against production and staging.

**A `200` does not always carry a token.** Accounts with a temporary password, or with two-step verification turned on, come back with a *challenge* instead: no token yet, and a `session` to answer it with at `POST /v1/signin/challenge`. Branch on the body, not on the status — `id_token` present means done, `challenge` present means one more step.



## OpenAPI

````yaml /api-reference/specs/auth/v1.json post /v1/signin
openapi: 3.0.1
info:
  title: Authentication API
  version: v1
  description: Single sign-in for every Cloud Humans API.
servers:
  - url: https://api.cloudhumans.com/auth
    description: Production
security: []
tags:
  - name: Authentication
    description: Exchange email and password for a token that works across every service.
paths:
  /v1/signin:
    post:
      tags:
        - Authentication
      summary: Sign in and get a token
      description: >-
        Authenticate with email and password and receive the token used by every
        other API. One token covers Claudia, Cloud Chat and the services that
        follow — the account you see is derived from it, and the same token is
        valid against production and staging.


        **A `200` does not always carry a token.** Accounts with a temporary
        password, or with two-step verification turned on, come back with a
        *challenge* instead: no token yet, and a `session` to answer it with at
        `POST /v1/signin/challenge`. Branch on the body, not on the status —
        `id_token` present means done, `challenge` present means one more step.
      operationId: signIn
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SignInRequest'
      responses:
        '200':
          description: Either a token, or a challenge to answer before one is issued.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/Token'
                  - $ref: '#/components/schemas/Challenge'
              examples:
                token:
                  summary: Signed in — token issued
                  value:
                    id_token: eyJraWQiOiJhYmMxMjMiLCJhbGciOiJSUzI1NiJ9...
                    access_token: eyJraWQiOiJkZWY0NTYiLCJhbGciOiJSUzI1NiJ9...
                    expires_in: 3600
                    token_type: Bearer
                sms_mfa:
                  summary: Two-step verification — a code was texted to the user
                  description: >-
                    The account has SMS verification on. A code is already on
                    its way to the phone registered on the account; collect it
                    and post it back with this `session`.
                  value:
                    challenge: SMS_MFA
                    session: AYABeF7x...
                new_password_required:
                  summary: Temporary password — a new one must be chosen
                  description: >-
                    First access, or a password that was reset for the user. No
                    code is sent; the user picks a password and it is posted
                    back with this `session`.
                  value:
                    challenge: NEW_PASSWORD_REQUIRED
                    session: AYABeF7x...
        '400':
          description: >-
            Wrong email or password, the account cannot sign in, or the body was
            missing `email`/`password`. Other `4xx` codes are possible and carry
            this same envelope, so match on the envelope rather than on a fixed
            status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Too many attempts. Wait before trying again.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security: []
components:
  schemas:
    SignInRequest:
      type: object
      required:
        - email
        - password
      properties:
        email:
          type: string
          format: email
          description: The same email you use to sign in to Cloud Chat.
          example: you@company.com
        password:
          type: string
          format: password
          description: Your password. It travels only on this call and is never logged.
          example: ••••••••
    Token:
      type: object
      description: Sign-in completed.
      required:
        - access_token
        - id_token
        - expires_in
        - token_type
      properties:
        id_token:
          type: string
          description: >-
            Send THIS one as `Authorization: Bearer <id_token>`. It carries the
            identity the APIs authorize on — your email and the accounts your
            credentials cover. The access token does not.
          example: eyJraWQiOiJhYmMxMjMiLCJhbGciOiJSUzI1NiJ9...
        access_token:
          type: string
          description: >-
            Issued by Cognito alongside the id token. Not what the Cloud Humans
            APIs read.
          example: eyJraWQiOiJkZWY0NTYiLCJhbGciOiJSUzI1NiJ9...
        expires_in:
          type: integer
          description: Lifetime in seconds.
          example: 3600
        token_type:
          type: string
          description: Always `Bearer`.
          example: Bearer
    Challenge:
      type: object
      description: >-
        No token yet — one more step first. Answer it at `POST
        /v1/signin/challenge`.
      required:
        - challenge
        - session
      properties:
        challenge:
          type: string
          description: >-
            What has to be answered.


            | Value | Means | You send back |

            |---|---|---|

            | `SMS_MFA` | Two-step verification is on for this account, and a
            code has just been texted to the phone registered on it. | `code` |

            | `NEW_PASSWORD_REQUIRED` | The password is temporary — first
            access, or it was reset. Nothing is sent to the user. |
            `new_password` |


            Those two are the ones this API can answer. Cognito has further
            challenge types and this field passes through whatever it returns,
            so treat an unrecognised value as "cannot finish here" rather than
            as impossible: an account configured that way — an authenticator
            app, for instance — has to use the Cloud Chat interface to get a
            token.
          example: SMS_MFA
        session:
          type: string
          description: >-
            Identifies this sign-in attempt, and is what proves the next call
            belongs to it. Pass it back unchanged. Scoped to a single sign-in
            attempt, and not something to store.
          example: AYABeF7x...
    Error:
      type: object
      required:
        - error
        - message
      properties:
        error:
          type: string
          description: >
            The Cognito exception type, or `AuthError` when the request was
            rejected before reaching Cognito.


            | `error` | What it means |

            |---|---|

            | `NotAuthorizedException` | On `/v1/signin`: wrong email or
            password. On `/v1/signin/challenge`: the `session` is no longer
            accepted. |

            | `CodeMismatchException` | Wrong SMS code **or** a session that has
            already expired — Cognito answers identically for both, so a client
            cannot tell them apart. Offer a retry, and a way back to sign-in for
            when retrying keeps failing. |

            | `ExpiredCodeException` | The code was right once, but too much
            time passed. Sign in again to get a fresh one. |

            | `InvalidPasswordException` | The new password does not meet the
            account's policy. |

            | `AuthError` | The gateway rejected the body: a missing field, or a
            `code` sent as a number instead of a string. `message` says which. |
          example: NotAuthorizedException
        message:
          type: string
          description: >-
            What went wrong, in words. Written for a developer reading a log,
            not for an end user — don't put it on screen verbatim.
          example: Incorrect username or password.

````