> ## 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.

# Answer a sign-in challenge

> Finish a sign-in that came back with a `challenge` instead of a token. Send the `session` from that response together with the answer the challenge asks for — the SMS code, or the new password.

Answer with the token in mind, not the step: this endpoint returns the **same body shape** as `/v1/signin`, so it can hand back another challenge rather than a token. A user on first access *with* two-step verification really does go `NEW_PASSWORD_REQUIRED` → `SMS_MFA` → token, each step handing you a fresh `session`. Loop until `id_token` shows up.

The `session` belongs to the sign-in attempt that produced it and is not built to be kept — there is nothing to persist and nothing to resume. Once it stops being accepted, call `/v1/signin` again, which sends a new code.



## OpenAPI

````yaml /api-reference/specs/auth/v1.json post /v1/signin/challenge
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/challenge:
    post:
      tags:
        - Authentication
      summary: Answer a sign-in challenge
      description: >-
        Finish a sign-in that came back with a `challenge` instead of a token.
        Send the `session` from that response together with the answer the
        challenge asks for — the SMS code, or the new password.


        Answer with the token in mind, not the step: this endpoint returns the
        **same body shape** as `/v1/signin`, so it can hand back another
        challenge rather than a token. A user on first access *with* two-step
        verification really does go `NEW_PASSWORD_REQUIRED` → `SMS_MFA` → token,
        each step handing you a fresh `session`. Loop until `id_token` shows up.


        The `session` belongs to the sign-in attempt that produced it and is not
        built to be kept — there is nothing to persist and nothing to resume.
        Once it stops being accepted, call `/v1/signin` again, which sends a new
        code.
      operationId: respondToSignInChallenge
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChallengeAnswer'
            examples:
              sms_mfa:
                summary: Answer SMS_MFA with the texted code
                value:
                  challenge: SMS_MFA
                  session: AYABeF7x...
                  email: you@company.com
                  code: '123456'
              new_password_required:
                summary: Answer NEW_PASSWORD_REQUIRED with the chosen password
                value:
                  challenge: NEW_PASSWORD_REQUIRED
                  session: AYABeF7x...
                  email: you@company.com
                  new_password: the-new-password
      responses:
        '200':
          description: Either a token, or the next challenge in the chain.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/Token'
                  - $ref: '#/components/schemas/Challenge'
              examples:
                token:
                  summary: Challenge cleared — token issued
                  value:
                    id_token: eyJraWQiOiJhYmMxMjMiLCJhbGciOiJSUzI1NiJ9...
                    access_token: eyJraWQiOiJkZWY0NTYiLCJhbGciOiJSUzI1NiJ9...
                    expires_in: 3600
                    token_type: Bearer
                chained_challenge:
                  summary: Cleared, but another step follows
                  description: >-
                    The new password was accepted and the account also has
                    two-step verification, so a code went out. Note the
                    `session` is a new one — answer with this, not with the one
                    you just used.
                  value:
                    challenge: SMS_MFA
                    session: AYABgH2p...
        '400':
          description: >-
            The answer was rejected, or the request was malformed. The two are
            told apart by `error`, and the common one is `CodeMismatchException`
            — see the table in the description of `error` below. 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'
              examples:
                code_mismatch:
                  summary: Wrong code, or the session already died
                  value:
                    error: CodeMismatchException
                    message: Invalid code or auth state for the user.
                invalid_password:
                  summary: New password rejected by the password policy
                  value:
                    error: InvalidPasswordException
                    message: Password does not conform to policy.
                missing_field:
                  summary: A required field never arrived
                  value:
                    error: AuthError
                    message: code is required for SMS_MFA, and must be a string
        '429':
          description: Too many attempts. Wait before trying again.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security: []
components:
  schemas:
    ChallengeAnswer:
      type: object
      description: >-
        Which field carries the answer depends on `challenge`. Sending the wrong
        one — or leaving it out — is a `400` before the request ever reaches
        Cognito.
      required:
        - challenge
        - session
        - email
      properties:
        challenge:
          type: string
          enum:
            - SMS_MFA
            - NEW_PASSWORD_REQUIRED
          description: Copy the `challenge` value from the response you are answering.
          example: SMS_MFA
        session:
          type: string
          description: The `session` from that same response, unchanged.
          example: AYABeF7x...
        email:
          type: string
          format: email
          description: >-
            The same email that was signed in with. The `session` alone does not
            identify the user to Cognito.
          example: you@company.com
        code:
          type: string
          description: >-
            **`SMS_MFA` only.** The code from the text message.


            Send it as a **string**, always. Codes can begin with a zero, and
            `"012345"` arriving as the JSON number `12345` is a different code —
            which is why a number here is rejected outright instead of being
            coerced into an intermittent "wrong code" for one user in ten.
          example: '123456'
        new_password:
          type: string
          format: password
          description: >-
            **`NEW_PASSWORD_REQUIRED` only.** The password the user chose. It
            has to satisfy the account's password policy — if it does not, the
            call comes back `400 InvalidPasswordException` and the user needs to
            pick another one. Retry with the same `session`; if that is refused
            too, the attempt is spent and the way back is `/v1/signin`.
          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.

````