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

# Sign in to CliqHub

> Signs the daemon in to CliqHub with a username and password or a personal token, and
settles which realm it belongs to. Send `credentials` to sign in; send only `realm`
(with or without `confirm_switch`) to finish a realm choice with the sign-in already stored.

What happens to the realm:
- **Already enrolled:** it stays in its realm. Asking for another realm is refused unless
  cliqd runs with `--allow-realm-override`; then the first call lists the teams that would
  be removed (`switch_requires_confirm`) and a second call with `confirm_switch: true` moves it.
- **New daemon, one realm (or `realm` given):** it enrolls there. If it already has teams or
  runs made offline, the first call lists them (`enroll_requires_confirm`) and nothing
  changes until you confirm.
- **New daemon, several realms, no `realm`:** `needs_realm_choice`; call again with `realm`.

A refused sign-in stores nothing and ends the session it opened on CliqHub. Moving realm
is refused while runs are going or work for the old realm has not synced.

From the CLI: `cliq login`.

No credential is needed from this machine. From another host, send `Authorization: Bearer` with a dispatch token CliqHub signed for this daemon or a `cliq_tok_…` personal token of a user in the daemon's realm; this only works once the daemon is connected to CliqHub. See [Authentication](/daemon-api/overview#authentication).



## OpenAPI

````yaml /openapi/daemon-by-tag/daemon-auth.yaml post /v1/login
openapi: 3.1.0
info:
  title: cliqd API
  version: 1.9.48
  description: >-
    HTTP API of `cliqd`, the local Cliq daemon. Generated from the daemon source
    by `scripts/generate_daemon_openapi.mjs` — do not edit by hand. See the
    [overview](/daemon-api/overview).
servers:
  - url: http://127.0.0.1:4900
    description: Local daemon (default port; `CLIQ_DAEMON_PORT` changes it)
security: []
tags:
  - name: Daemon Sign-in
    description: >-
      Signing this daemon in to CliqHub, choosing the realm it belongs to, and
      minting CliqHub

      tokens with its sign-in. `cliq login` and `cliq logout` use these
      endpoints.


      A daemon that has never joined a realm **enrolls** in one the first time
      it signs in. Once

      enrolled it stays in that realm: signing in again keeps it, and moving it
      to another realm

      needs cliqd started with `--allow-realm-override` plus an explicit
      confirmation. Sign-in

      and sign-out are only accepted from the machine running cliqd.
paths:
  /v1/login:
    post:
      tags:
        - Daemon Sign-in
      summary: Sign in to CliqHub
      description: >-
        Signs the daemon in to CliqHub with a username and password or a
        personal token, and

        settles which realm it belongs to. Send `credentials` to sign in; send
        only `realm`

        (with or without `confirm_switch`) to finish a realm choice with the
        sign-in already stored.


        What happens to the realm:

        - **Already enrolled:** it stays in its realm. Asking for another realm
        is refused unless
          cliqd runs with `--allow-realm-override`; then the first call lists the teams that would
          be removed (`switch_requires_confirm`) and a second call with `confirm_switch: true` moves it.
        - **New daemon, one realm (or `realm` given):** it enrolls there. If it
        already has teams or
          runs made offline, the first call lists them (`enroll_requires_confirm`) and nothing
          changes until you confirm.
        - **New daemon, several realms, no `realm`:** `needs_realm_choice`; call
        again with `realm`.


        A refused sign-in stores nothing and ends the session it opened on
        CliqHub. Moving realm

        is refused while runs are going or work for the old realm has not
        synced.


        From the CLI: `cliq login`.


        No credential is needed from this machine. From another host, send
        `Authorization: Bearer` with a dispatch token CliqHub signed for this
        daemon or a `cliq_tok_…` personal token of a user in the daemon's realm;
        this only works once the daemon is connected to CliqHub. See
        [Authentication](/daemon-api/overview#authentication).
      operationId: daemon_post_login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                credentials:
                  description: >-
                    Provider credentials (username+password, or a user PAT as
                    token); omit to reuse the stored session
                  type: object
                  propertyNames:
                    type: string
                  additionalProperties: {}
                realm:
                  description: >-
                    Realm to enroll in / switch to (`org.slug`); omit to keep
                    the current one or get the list to choose from
                  type: string
                  minLength: 1
                confirm_switch:
                  description: >-
                    Confirm the realm change: moving an enrolled daemon (needs
                    --allow-realm-override) after seeing teams_to_remove, or
                    enrolling with local teams/runs after seeing
                    enroll_requires_confirm
                  type: boolean
            examples:
              password:
                summary: Username and password
                value:
                  credentials:
                    username: sshah
                    password: <your-password>
              token:
                summary: A personal token, choosing the realm
                value:
                  credentials:
                    token: cliq_tok_4f9a…
                  realm: acme.production
              confirm:
                summary: Confirm a realm change
                value:
                  realm: acme.staging
                  confirm_switch: true
      responses:
        '200':
          description: Success.
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - data
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    $ref: '#/components/schemas/LoginResult'
              examples:
                signed_in:
                  summary: Signed in, realm kept
                  value:
                    ok: true
                    data:
                      identity: sshah
                      expires_at: null
                      realm: acme.production
                      realm_locked: true
                      enrolled: false
                      authorized_realms:
                        - slug: production
                          qualified_slug: acme.production
                          name: Production
                          active: true
                        - slug: staging
                          qualified_slug: acme.staging
                          name: Staging
                          active: false
                      default_realm: acme.production
                choose:
                  summary: New daemon, choose a realm
                  value:
                    ok: true
                    data:
                      identity: sshah
                      expires_at: null
                      realm: null
                      realm_locked: false
                      enrolled: false
                      authorized_realms:
                        - slug: production
                          qualified_slug: acme.production
                          name: Production
                          active: false
                        - slug: staging
                          qualified_slug: acme.staging
                          name: Staging
                          active: false
                      default_realm: acme.production
                      needs_realm_choice: true
        '400':
          description: >-
            `BAD_REQUEST` — Neither `credentials` nor `realm` was sent,
            `credentials` has neither username+password nor `token`, the token
            is not a `cliq_tok_…` user token, or the body is not valid JSON.


            `PAYLOAD_INVALID` — The body (query string for `GET`) fails the
            request schema. `details` lists each issue.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorReply'
              examples:
                BAD_REQUEST:
                  summary: BAD_REQUEST
                  value:
                    ok: false
                    error:
                      code: BAD_REQUEST
                      message: credentials or realm is required
                PAYLOAD_INVALID:
                  summary: PAYLOAD_INVALID
                  value:
                    ok: false
                    error:
                      code: PAYLOAD_INVALID
                      message: Payload failed schema validation
        '401':
          description: >-
            `HUB_LOGIN_FAILED` — CliqHub rejected the username or password.


            `NOT_LOGGED_IN` — Only `realm` was sent but no sign-in is stored, or
            the stored session has expired.


            `UNAUTHORIZED` — A caller on another host sent no `Authorization:
            Bearer` token, or one that does not verify (expired, wrong
            audience).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorReply'
              examples:
                HUB_LOGIN_FAILED:
                  summary: HUB_LOGIN_FAILED
                  value:
                    ok: false
                    error:
                      code: HUB_LOGIN_FAILED
                      message: Invalid username or password
                NOT_LOGGED_IN:
                  summary: NOT_LOGGED_IN
                  value:
                    ok: false
                    error:
                      code: NOT_LOGGED_IN
                      message: Not logged in to CliqHub
                UNAUTHORIZED:
                  summary: UNAUTHORIZED
                  value:
                    ok: false
                    error:
                      code: UNAUTHORIZED
                      message: Missing or invalid Authorization Bearer token
        '403':
          description: >-
            `SETTING_LOCAL_ONLY` — The request came from another host. Sign the
            daemon in on its own machine.


            `LOGIN_NO_REALMS` — The user is not a member of any realm.


            `LOGIN_NOT_MEMBER` — The user is not a member of the realm this
            daemon is in.


            `LOGIN_REALM_LOCKED` — The daemon is enrolled and `realm` names
            another realm, but cliqd was not started with
            `--allow-realm-override`.


            `WEB_REQUEST_REFUSED` — The request carries an `Origin` header (it
            came from a web page).


            `HOST_NOT_LOCAL` — A loopback caller addressed a `Host` other than
            localhost (DNS rebinding guard).


            `LOCAL_ONLY` — The daemon isn't connected to CliqHub (no dispatch
            key) and the caller is on another host.


            `FORBIDDEN` — A `cliq_tok_…` token's user is not in this daemon's
            realm ACL, or the ACL is not loaded yet.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorReply'
              examples:
                SETTING_LOCAL_ONLY:
                  summary: SETTING_LOCAL_ONLY
                  value:
                    ok: false
                    error:
                      code: SETTING_LOCAL_ONLY
                      message: Sign cliqd in on the machine running it
                LOGIN_NO_REALMS:
                  summary: LOGIN_NO_REALMS
                  value:
                    ok: false
                    error:
                      code: LOGIN_NO_REALMS
                      message: You aren't a member of any realm on CliqHub
                LOGIN_NOT_MEMBER:
                  summary: LOGIN_NOT_MEMBER
                  value:
                    ok: false
                    error:
                      code: LOGIN_NOT_MEMBER
                      message: >-
                        You aren't a member of acme.production, the realm this
                        cliqd is in
                LOGIN_REALM_LOCKED:
                  summary: LOGIN_REALM_LOCKED
                  value:
                    ok: false
                    error:
                      code: LOGIN_REALM_LOCKED
                      message: >-
                        cliqd is in acme.production; moving it to acme.staging
                        needs --allow-realm-override
                WEB_REQUEST_REFUSED:
                  summary: WEB_REQUEST_REFUSED
                  value:
                    ok: false
                    error:
                      code: WEB_REQUEST_REFUSED
                      message: cliqd doesn't accept requests from web pages
                HOST_NOT_LOCAL:
                  summary: HOST_NOT_LOCAL
                  value:
                    ok: false
                    error:
                      code: HOST_NOT_LOCAL
                      message: cliqd only answers requests addressed to localhost
                LOCAL_ONLY:
                  summary: LOCAL_ONLY
                  value:
                    ok: false
                    error:
                      code: LOCAL_ONLY
                      message: >-
                        This cliqd isn't connected to CliqHub yet, so from
                        another machine it only accepts a local token (minted by
                        tokens/create on the machine running cliqd)
                FORBIDDEN:
                  summary: FORBIDDEN
                  value:
                    ok: false
                    error:
                      code: FORBIDDEN
                      message: Access denied — identity not in realm ACL
        '404':
          description: >-
            `LOGIN_REALM_NOT_FOUND` — `realm` is not one of the user's realms.
            `details` lists them.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorReply'
              examples:
                LOGIN_REALM_NOT_FOUND:
                  summary: LOGIN_REALM_NOT_FOUND
                  value:
                    ok: false
                    error:
                      code: LOGIN_REALM_NOT_FOUND
                      message: Realm "acme.staging" isn't one of your realms
        '409':
          description: >-
            `LOGIN_REALM_AMBIGUOUS` — `realm` matches more than one of the
            user's realms; use the full `org.realm` name.


            `LOGIN_REALM_PINNED` — The daemon's realm is fixed by the
            `CLIQ_DAEMON_TOKEN` environment variable.


            `LOGIN_RUNS_ACTIVE` — Enrolling or moving realm while runs are going
            on this daemon.


            `LOGIN_TEAMS_NOT_IN_REALM` — Teams on this daemon are not on the
            target realm's team list. `details` lists them. Uninstall them or
            have a realm admin add them.


            `LOGIN_UNSYNCED_WORK` — Moving realm while runs or teams for the
            current realm have not reached CliqHub yet; nothing was changed. Try
            again once the daemon has synced.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorReply'
              examples:
                LOGIN_REALM_AMBIGUOUS:
                  summary: LOGIN_REALM_AMBIGUOUS
                  value:
                    ok: false
                    error:
                      code: LOGIN_REALM_AMBIGUOUS
                      message: '"staging" matches more than one of your realms'
                LOGIN_REALM_PINNED:
                  summary: LOGIN_REALM_PINNED
                  value:
                    ok: false
                    error:
                      code: LOGIN_REALM_PINNED
                      message: cliqd's realm is set by CLIQ_DAEMON_TOKEN
                LOGIN_RUNS_ACTIVE:
                  summary: LOGIN_RUNS_ACTIVE
                  value:
                    ok: false
                    error:
                      code: LOGIN_RUNS_ACTIVE
                      message: >-
                        1 run is in progress on this cliqd — moving it to
                        another realm would stop it
                LOGIN_TEAMS_NOT_IN_REALM:
                  summary: LOGIN_TEAMS_NOT_IN_REALM
                  value:
                    ok: false
                    error:
                      code: LOGIN_TEAMS_NOT_IN_REALM
                      message: acme/code-review isn't in acme.staging
                LOGIN_UNSYNCED_WORK:
                  summary: LOGIN_UNSYNCED_WORK
                  value:
                    ok: false
                    error:
                      code: LOGIN_UNSYNCED_WORK
                      message: >-
                        3 items for acme.production haven't synced yet — nothing
                        was changed
        '500':
          description: >-
            `INTERNAL_ERROR` — CliqHub could not be reached or failed (the
            message is CliqHub's when it gave one), or enrolling the daemon
            failed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorReply'
              examples:
                INTERNAL_ERROR:
                  summary: INTERNAL_ERROR
                  value:
                    ok: false
                    error:
                      code: INTERNAL_ERROR
                      message: Login failed
        '503':
          description: >-
            `LOGIN_REALM_TEAMS_UNAVAILABLE` — The target realm's team list could
            not be read from CliqHub; nothing was changed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorReply'
              examples:
                LOGIN_REALM_TEAMS_UNAVAILABLE:
                  summary: LOGIN_REALM_TEAMS_UNAVAILABLE
                  value:
                    ok: false
                    error:
                      code: LOGIN_REALM_TEAMS_UNAVAILABLE
                      message: >-
                        Couldn't read the team list of acme.staging — nothing
                        was changed
      security:
        - {}
        - DaemonBearer: []
components:
  schemas:
    LoginResult:
      type: object
      description: >
        What happened. Three replies ask you to call again:

        `needs_realm_choice` (pick a `realm`), `switch_requires_confirm` and

        `enroll_requires_confirm` (send the same `realm` with `confirm_switch:
        true`).
      required:
        - identity
        - expires_at
        - realm
        - realm_locked
        - enrolled
        - authorized_realms
        - default_realm
      properties:
        identity:
          type: string
          description: The CliqHub username the daemon now acts for.
          example: sshah
        expires_at:
          type: 'null'
          description: Always `null` today.
        realm:
          type:
            - string
            - 'null'
          description: >-
            The daemon's realm (`org.realm`) after this call; `null` while a new
            daemon still has to choose.
          example: acme.production
        realm_locked:
          type: boolean
          description: >-
            The daemon is enrolled and cliqd runs without
            `--allow-realm-override`, so it cannot move realm.
          example: true
        enrolled:
          type: boolean
          description: >-
            This call enrolled the daemon or moved it to another realm; the
            daemon restarts its CliqHub connection.
          example: false
        authorized_realms:
          type: array
          description: The user's realms; `active` marks this daemon's.
          items:
            $ref: '#/components/schemas/LoginRealm'
        default_realm:
          type:
            - string
            - 'null'
          description: The user's default realm (`org.realm`).
          example: acme.production
        needs_realm_choice:
          type: boolean
          description: >-
            A new daemon and several realms to choose from. Call again with
            `realm`.
        switch_requires_confirm:
          type: boolean
          description: >-
            Moving realms would uninstall `teams_to_remove`. Call again with
            `confirm_switch: true`.
        enroll_requires_confirm:
          type: boolean
          description: >-
            Enrolling would bring this daemon's teams (`local_teams`) and
            unsynced runs into `target_realm`. Call again with `confirm_switch:
            true`.
        teams_to_remove:
          type: array
          items:
            type: string
          description: >-
            Teams (`scope/slug`) that are not on the target realm's list and
            would be removed.
        target_realm:
          type: string
          description: The realm being enrolled in.
        local_teams:
          type: array
          items:
            type: string
          description: Teams that would join the realm.
        unsynced_runs:
          type: integer
          description: Runs CliqHub has not received yet that would sync into the realm.
    ErrorReply:
      type: object
      description: >-
        Every failure. Switch on `error.code` — it is stable; `message` is for
        people and may change.
      required:
        - ok
        - error
      properties:
        ok:
          type: boolean
          const: false
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Stable error code. See [Errors](/daemon-api/errors).
              example: TEAM_NOT_FOUND
            message:
              type: string
              description: What went wrong and, where possible, what to do.
              example: Team 'acme/code-review' not found
            details:
              description: >-
                Structured detail when there is some: the list of schema issues
                for `PAYLOAD_INVALID`, the agent problems for
                `TEAM_AGENT_CONFIG_REQUIRED`, …
    LoginRealm:
      type: object
      required:
        - slug
        - qualified_slug
        - name
        - active
      properties:
        slug:
          type: string
          example: production
        qualified_slug:
          type: string
          description: '`org.realm`, the name to send as `realm`.'
          example: acme.production
        name:
          type:
            - string
            - 'null'
          example: Production
        active:
          type: boolean
          description: This daemon is in this realm.
          example: true
  securitySchemes:
    DaemonBearer:
      type: http
      scheme: bearer
      description: >-
        Not needed from the same machine. From another host: a dispatch JWT
        CliqHub signed for this daemon, or a `cliq_tok_…` personal token whose
        user is in the daemon's realm.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.