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

# API программного входа: токены аутентификации

> POST /programmatic/login/ для headless-аутентификации в Didit. Выдаёт токены доступа без 2FA. Для AI-агентов и MCP, оплата за использование.



Инструкция для программного входа
        Скопировать промпт в буфер обмена



      
      

\<pre>

\`Exchange a verified Didit account's email + password for a fresh JWT access token (headless, no browser).

Endpoint:
POST https://apx.didit.me/auth/v2/programmatic/login/

Authentication:
None. Email + password is the credential.

Request body:
\- email (string, required) — Same email used at /programmatic/register/.
\- password (string, required) — Plain password (HTTPS only).

Account requirements:
The account must have a verified email and password. Accounts created through POST /programmatic/register/ work immediately after email verification. Console-created email/password accounts can also use this endpoint. OAuth-only console accounts must set a password first.

curl example:
curl -X POST https://apx.didit.me/auth/v2/programmatic/login/ \
-H "Content-Type: application/json" \
-d '{
"email": "you@yourdomain.com",
"password": "MyStr0ng!Pass"
}'

Response (200 OK):
\- access_token (string) — RS256 JWT. Pass as "Authorization: Bearer <access_token>" on every Account Management endpoint at apx.didit.me/auth/v2. DO NOT use against verification.didit.me — that host uses x-api-key.
\- refresh_token (string) — RS256 JWT for refresh flows.
\- expires_in (integer) — Default 86400 (24 hours).

Token shape:
\- 24-hour default lifetime.
\- RS256-signed.
\- No browser session is required.

Brute-force protection:
\- Progressive account lockout per email: 5 consecutive failures = 15 min lockout, 10 = 1 hour, 20 = 24 hours.
\- Per-IP rate limit: 20 attempts/minute and 100 attempts/hour.

Failure modes:
\- 400 — { "email": ["..."] } / { "password": ["..."] } field-level errors.
\- 401 — { "detail": "Invalid email or password." }
\- 403 — { "detail": "..." } when SSO policy blocks password login for the account.
\- 429 — { "detail": "Too many login attempts.", "wait": <seconds> } on lockout or per-IP limit. Honour the wait value before retrying.

Recovering your api_key:
After a successful login, call GET /organizations/me/ to enumerate organizations and applications, then GET /organizations/me/{org_id}/applications/{app_id}/ to read the api_key — see /auth-api/get-credentials.

Where to use access_token vs api_key:
\- access_token (Bearer) → https://apx.didit.me/auth/v2/... (org / app management).
\- api_key (x-api-key) → https://verification.didit.me/v3/... (sessions, workflows, billing).

For end-to-end Didit integration, paste in the full prompt at /integration/integration-prompt.`

\</pre>



      
      

Вставьте в



## OpenAPI

````yaml ru/openapi-auth.json POST /programmatic/login/
openapi: 3.0.0
info:
  version: 1.0.0
  title: Didit Auth API
  description: >-
    API программной регистрации и аутентификации для разработчиков и AI-агентов.
    Регистрация за 2 вызова API: без использования браузера. Самая удобная для
    агентов платформа верификации личности.


    ## Базовый URL


    Все endpoint в этой спецификации размещены по адресу
    `https://apx.didit.me/auth/v2`. Это другой хост, отличный от API верификации
    (`https://verification.didit.me/v3`). Не путайте их: этот хост
    аутентификации выдаёт ваши **токены доступа JWT** и позволяет просматривать
    список и управлять вашими **организациями** и **приложениями**. На хосте
    верификации вы вызываете сессии, сценарии, AML и т. д., аутентифицируясь с
    помощью **`api_key`**, возвращаемого этим API.


    ## Как устроен процесс аутентификации


    1. `POST /programmatic/register/`: отправьте email + пароль.

    2. Получите 6-значный буквенно-цифровой код по электронной почте.

    3. `POST /programmatic/verify-email/`: отправьте код. Вы получите обратно
    JWT `access_token`, `refresh_token`, организацию и стандартное приложение с
    `client_id` + `api_key`.

    4. Сохраните `api_key`. Используйте его в качестве `x-api-key` для каждого
    вызова к `https://verification.didit.me/v3/...`.

    5. В дальнейшем с любой машины `POST /programmatic/login/` обменивает ваши
    email + пароль на свежий JWT, после чего вызовите `GET /organizations/me/` и
    endpoint приложений для восстановления или ротации учётных данных.


    JWT требуется только для endpoint **Управления аккаунтом** в этой
    спецификации; трафик к API верификации использует долгоживущий `api_key`, а
    не JWT.


    ## Соглашения


    - Все тела запросов и ответов представляют собой JSON (`Content-Type:
    application/json`).

    - **Используйте реальный, доставляемый адрес электронной почты при
    тестировании `POST /programmatic/register/`.** Endpoint отправляет код
    подтверждения синхронно, поэтому адреса на зарезервированных тестовых
    доменах (`@example.com`, `@example.org`, `@*.test`, `@*.example`,
    `@*.invalid`) отклоняются службой доставки почты, и запрос возвращает `500`.
    Замените плейсхолдер `you@yourdomain.com` в примерах на почтовый ящик,
    который вы действительно можете прочитать.

    - Ошибки валидации возвращают HTTP `400` в одном из двух форматов:
    `{"field": ["message", ...]}` для проблем на уровне полей или `{"detail":
    "message"}` для ошибок бизнес-правил. Некоторые валидаторы (сложность пароля
    при регистрации, истечение срока действия кода при проверке email) в
    настоящее время возвращают простой массив `["message"]` вместо структуры с
    ключами полей - это известная несогласованность на стороне сервера, поэтому
    клиентский код должен принимать оба формата.

    - Ошибки аутентификации возвращаются в виде `{"detail": "..."}` с кодом HTTP
    `401` (недействительный bearer token) или `403` (отсутствует bearer token /
    недостаточно прав).

    - Ответы с ограничением частоты запросов содержат поле `wait` с временем
    задержки в секундах.
servers:
  - url: https://apx.didit.me/auth/v2
    description: Продакшн-сервер аутентификации
security: []
tags:
  - name: Programmatic Auth
    description: >-
      Жизненный цикл аккаунта с электронной почтой и паролем: регистрация,
      подтверждение, вход. Возвращает JWT `access_token`, который вы используете
      для endpoint'ов управления аккаунтом, а также (при подтверждении)
      `client_id` и `api_key` вашего первого приложения.
  - name: Account Management
    description: >-
      Аутентифицированные endpoint (JWT bearer), которые возвращают список
      организаций и позволяют создавать, получать или обновлять приложения.
      Используйте их для восстановления или ротации `api_key`, с помощью
      которого вы аутентифицируете вызовы API верификации.
paths:
  /programmatic/login/:
    post:
      tags:
        - Programmatic Auth
      summary: Программный вход
      description: >-
        Аутентифицировать любой аккаунт Didit с подтверждённым адресом
        электронной почты и паролем и получить пару JWT RS256 (срок действия по
        умолчанию: 86400 с). Созданные в консоли аккаунты с почтой и паролем
        могут использовать этот endpoint; для аккаунтов консоли, созданных
        только через OAuth, сначала необходимо установить пароль.
      operationId: programmatic_login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginRequest'
            examples:
              Default:
                summary: Программный вход
                value:
                  email: you@yourdomain.com
                  password: MyStr0ng!Pass
      responses:
        '200':
          description: >-
            Вход выполнен успешно. Возвращает свежую пару JWT. Используйте
            `access_token` в качестве `Authorization: Bearer <token>` при
            вызовах к `/organizations/me/...`, чтобы получить `org_id`, `app_id`
            и `api_key`. Если в аккаунте настроено приложение-аутентификатор или
            ключ доступа, ответ содержит `2fa_required: true` и `temp_token`
            вместо токенов.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
              examples:
                Success:
                  value:
                    access_token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
                    refresh_token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
                    expires_in: 86400
                    message: Login successful
        '400':
          description: Ошибка валидации. Проблемы на уровне полей в теле запроса.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
              examples:
                MissingFields:
                  summary: Пустое тело
                  value:
                    email:
                      - This field is required.
                    password:
                      - This field is required.
                InvalidEmail:
                  summary: Некорректный email
                  value:
                    email:
                      - Enter a valid email address.
        '401':
          description: В аутентификации отказано.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthError'
              examples:
                InvalidCredentials:
                  summary: >-
                    Электронная почта и пароль не совпали (также возвращается,
                    если аккаунт не существует, во избежание перебора
                    пользователей)
                  value:
                    detail: Invalid email or password.
                NotVerified:
                  summary: >-
                    Аккаунт существует, но адрес электронной почты ещё не
                    подтверждён
                  value:
                    detail: Please verify your email address before logging in.
                SSORequired:
                  summary: Учётная запись принадлежит организации, требующей SSO
                  value:
                    detail:
                      error: sso_required
                      message: Your organization 'Acme Corp' requires SSO login.
                      organization_name: Acme Corp
                      organization_slug: acme-corp
                      sso_url: /sso/acme-corp
        '403':
          description: >-
            Учётная запись была восстановлена и должна настроить новый способ
            входа перед тем, как сможет войти. Ссылка для этого была отправлена
            на адрес учётной записи. Учётная запись, организация которой требует
            SSO, получает `403` с `error: sso_required`, `organization_name`,
            `organization_slug` и `sso_url`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecoverySetupRequiredError'
              examples:
                RecoverySetupRequired:
                  summary: Восстановленный аккаунт без настроенного способа входа
                  value:
                    recovery_setup_required: true
                    setup_link_sent: true
                    message: >-
                      You recovered your account. Open the link we emailed you
                      to set up a new sign-in method.
                    code: recovery_setup_required
        '429':
          description: >-
            Аккаунт заблокирован (слишком много недавних неудачных попыток для
            этого адреса электронной почты) или превышен лимит запросов для IP
            (слишком много попыток с этого адреса). Также возвращается с
            заголовком `Retry-After: 5`, когда сервис временно проверяет
            максимально возможное количество паролей или кодов одновременно;
            повторите попытку через указанное количество секунд.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
              examples:
                AccountLocked:
                  summary: Сработала прогрессивная блокировка
                  value:
                    detail: >-
                      Account temporarily locked due to too many failed login
                      attempts. Please try again later.
                IPRateLimited:
                  summary: Более 20/мин или 100/час с этого IP-адреса
                  value:
                    detail: >-
                      Too many login attempts from this IP address. Please wait
                      before trying again.
                PasswordChecksBusy:
                  summary: Слишком много проверок пароля выполняется одновременно
                  value:
                    detail: >-
                      Too many sign-in attempts are being processed right now.
                      Please try again in a few seconds.
      security:
        - {}
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl -X POST https://apx.didit.me/auth/v2/programmatic/login/ \
              -H "Content-Type: application/json" \
              -d '{
                "email": "you@yourdomain.com",
                "password": "MyStr0ng!Pass"
              }'
        - lang: python
          label: Python
          source: |-
            from didit import Didit

            # This call needs no API key.
            client = Didit()

            result = client.account.login(
                email="you@yourdomain.com",
                password="MyStr0ng!Pass",
            )
            print(result)
        - lang: javascript
          label: Node.js
          source: |-
            import Didit from "@didit-protocol/sdk-node";

            // This call needs no API key.
            const didit = new Didit();

            const result = await didit.account.login({
              email: "you@yourdomain.com",
              password: "MyStr0ng!Pass",
            });
            console.log(result);
        - lang: go
          label: Go
          source: "ctx := context.Background()\n// This call needs no API key.\nclient := didit.NewClient()\n\nresult, err := client.Account.Login(ctx, didit.LoginRequest{\n\tEmail:    \"you@yourdomain.com\",\n\tPassword: \"MyStr0ng!Pass\",\n})\nif err != nil {\n\tlog.Fatal(err)\n}\nfmt.Printf(\"%+v\\n\", result)"
        - lang: java
          label: Java
          source: |-
            import me.didit.Didit;
            import me.didit.types.LoginRequest;

            // This call needs no API key.
            Didit client = new Didit();

            var result = client.account().login(LoginRequest.builder()
                    .email("you@yourdomain.com")
                    .password("MyStr0ng!Pass")
                    .build());
            System.out.println(result);
        - lang: php
          label: PHP
          source: |-
            <?php

            require 'vendor/autoload.php';

            use Didit\DiditClient;

            // This call needs no API key.
            $didit = new DiditClient();

            $result = $didit->account->login([
                'email' => 'you@yourdomain.com',
                'password' => 'MyStr0ng!Pass',
            ]);
            print_r($result->toArray());
        - lang: ruby
          label: Ruby
          source: |-
            require "didit"

            # This call needs no API key.
            didit = Didit::Client.new

            result = didit.account.login(
              email: "you@yourdomain.com",
              password: "MyStr0ng!Pass"
            )
            puts result.to_h
        - lang: csharp
          label: .NET
          source: |-
            using System;
            using System.Text.Json;
            using Didit;

            // This call needs no API key.
            using var client = new DiditClient();

            var result = await client.Account.LoginAsync(new LoginRequest
            {
                Email = "you@yourdomain.com",
                Password = "MyStr0ng!Pass",
            });
            Console.WriteLine(JsonSerializer.Serialize(result));
components:
  schemas:
    LoginRequest:
      type: object
      required:
        - email
        - password
      properties:
        email:
          type: string
          format: email
          example: you@yourdomain.com
        password:
          type: string
          example: MyStr0ng!Pass
    LoginResponse:
      type: object
      properties:
        access_token:
          type: string
          description: >-
            Подписанный по алгоритму RS256 JWT. Используйте как `Authorization:
            Bearer <token>` для endpoints управления аккаунтом.
        refresh_token:
          type: string
        expires_in:
          type: integer
          description: Срок действия токена доступа в секундах (по умолчанию 86400).
          example: 86400
        message:
          type: string
          example: Login successful
    ValidationError:
      description: >-
        Тело ошибки валидации. Предполагаемый формат обёртки представляет собой
        словарь `{field: ["message", ...]}` для ошибок на уровне полей или
        одноключевую обёртку `{detail: "..."}` для ошибок бизнес-правил.
        Некоторые валидаторы (стойкость пароля при регистрации, действительность
        кода при verify-email) в настоящее время возвращают простой массив
        `["message"]` (известная несогласованность на стороне сервера). Схема
        ниже сохраняет эту ветку с простым массивом, чтобы клиентский код,
        сгенерированный из этой спецификации, мог десериализовать текущие
        ответы, однако новому клиентскому коду следует обрабатывать её в формате
        с ключом соответствующего поля (`{password: [...]}` или `{code:
        [...]}`), и контракт сервера со временем придёт к этому виду.
      oneOf:
        - type: object
          additionalProperties:
            type: array
            items:
              type: string
          example:
            email:
              - Enter a valid email address.
        - type: object
          properties:
            detail:
              type: string
          example:
            detail: An account with this email already exists.
        - type: array
          items:
            type: string
          example:
            - Invalid or expired verification code.
    AuthError:
      type: object
      description: Оболочка ошибки аутентификации, авторизации или отсутствия ресурса.
      properties:
        detail:
          type: string
          example: Invalid access token
    RecoverySetupRequiredError:
      type: object
      description: >-
        Учётная запись была восстановлена, и для неё ещё не настроен способ
        входа. Токены не выдаются; одноразовая ссылка для настройки нового
        способа была отправлена на адрес учётной записи. Откройте её, настройте
        способ и войдите снова.
      required:
        - recovery_setup_required
        - setup_link_sent
        - message
        - code
      properties:
        recovery_setup_required:
          type: boolean
          example: true
        setup_link_sent:
          type: boolean
          example: true
        message:
          type: string
          example: >-
            You recovered your account. Open the link we emailed you to set up a
            new sign-in method.
        code:
          type: string
          enum:
            - recovery_setup_required
          example: recovery_setup_required
    RateLimitError:
      type: object
      description: >-
        Ответ о превышении лимита запросов или прогрессивной блокировке. `wait`
        указывает время ожидания в секундах.
      properties:
        detail:
          type: string
          example: >-
            Too many login attempts from this IP address. Please wait before
            trying again.

````

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