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

# Token Endpoint: Tukar Kode dan Refresh Token | Didit

> Didit: POST /token/ menukar kode otorisasi PKCE atau memutar refresh token. Menggunakan ulang refresh token akan mencabut sesi.

> [!WARNING]
>
> Autentikasi adalah fitur baru. Endpoint ini dilayani oleh penerbit aplikasi Anda, misalnya `https://<name>.auth.didit.me`, dan tidak pernah menerima kunci API Didit Anda.

Token penyegaran hanya dapat digunakan sekali. Jika token penyegaran yang sudah dipakai dikirim lagi, seluruh sesi akan dicabut, jadi jalankan satu proses penyegaran pada satu waktu untuk setiap sesi dan ganti ketiga token sekaligus. Lihat [Token penyegaran](https://docs.didit.me/id/authentication/tokens-and-jwks.md#refresh-token).


## OpenAPI

````yaml id/openapi-authentication.json POST /token/
openapi: 3.0.3
info:
  title: Didit Authentication API
  version: 1.0.0
  description: >-
    Endpoint yang menghadap ke server dari Didit Authentication, produk masuk
    (sign-in) untuk pengguna akhir aplikasi Anda. Authentication adalah fitur
    **baru** dan akan tersedia di lingkungan produksi pada rilis 2026-11-01.


    ## Base URL


    Setiap aplikasi adalah penerbit (issuer) OpenID Connect miliknya sendiri,
    dan setiap endpoint di bawah ini dilayani di root dari penerbit tersebut:


    - `https://<name>.auth.didit.me` untuk aplikasi live, atau
    `https://<name>.sandbox.auth.didit.me` untuk aplikasi sandbox.

    - Domain kustom Anda sendiri, setelah aktif. Domain tersebut kemudian
    menggantikan host default sebagai penerbit: token baru mencantumkannya di
    `iss`, discovery mengarah ke sana, dan kunci yang dapat dipublikasikan
    berubah. Token akses yang diterbitkan di bawah host default akan gagal di
    `/session/`, `/step-up/`, dan `/monitoring-token/` hingga token tersebut
    diperbarui.


    Lingkungan live dan sandbox adalah aplikasi terpisah, sehingga keduanya
    merupakan penerbit terpisah dengan kunci terpisah. Kunci yang dapat
    dipublikasikan adalah `pk_live_` (live) atau `pk_test_` (sandbox) diikuti
    oleh encoding base64url tanpa padding dari penerbit, tanpa garis miring
    penutup: lakukan decode untuk menemukan penerbitnya. Kunci ini adalah
    pengidentifikasi publik, bukan kredensial.


    Endpoint JSON yang sama juga dilayani di bawah
    `https://apx.didit.me/auth/v2/authentication/{application_id}/`. Token
    selalu mencantumkan penerbit di atas, alamat mana pun yang menerbitkannya.


    ## Kredensial


    Endpoint ini tidak pernah menerima kunci API Didit Anda atau token konsol.


    - Discovery, JWKS, dan `POST /token/` tidak memerlukan kredensial. Klien
    bersifat publik: tidak ada client secret, dan kode otorisasi terikat pada
    verifier PKCE-nya (`S256`). `client_id` adalah ID aplikasi Anda.

    - `/session/`, `/step-up/`, dan `/monitoring-token/` menerima token akses
    pengguna: `Authorization: Bearer <access_token>`. Setiap panggilan yang
    berhasil dihitung sebagai aktivitas pada sesi, sehingga memulai ulang batas
    waktu tidak aktif (idle timeout) sesi tersebut.


    ## Body permintaan


    Setiap endpoint yang menerima body mendukung format JSON, form-encoded, dan
    multipart. Dokumen ini mencantumkan JSON, ditambah form-encoded untuk `POST
    /token/` sebagaimana yang dikirimkan oleh klien OAuth. Kolom yang tidak
    diterima oleh endpoint akan menghasilkan `400 {"<field>": "unknown_field"}`.


    ## Token


    - **Token akses**: JWT ES256 dengan header `typ: at+jwt`, berlaku paling
    lama 5 menit dan tidak pernah lebih lama dari sesinya. Klaim:
    `AccessTokenClaims`.

    - **Token ID**: JWT ES256 dengan header `typ: JWT`. Klaim: `IDTokenClaims`.
    Token ini tidak pernah diterima sebagai token akses.

    - **Token pembaruan**: string acak yang dirotasi pada setiap penggunaan.
    Mengirimkan token pembaruan yang sudah digunakan kembali akan mencabut sesi.


    Verifikasi token dengan kunci dari `GET /.well-known/jwks.json`: algoritma
    harus berupa `ES256`, `typ` harus cocok dengan tipe token, `iss` harus sama
    dengan penerbit yang Anda konfigurasikan, dan `aud` harus sama dengan ID
    aplikasi Anda. Verifikasi offline hanya melihat sesi yang dicabut setelah
    token aksesnya kedaluwarsa; `GET /session/` adalah pemeriksaan instan.


    ## Error


    | Body | Kapan |

    | --- | --- |

    | `{"error": "<code>"}` | Error dari `POST /token/`, semuanya dengan status
    `400`, dalam bentuk body [RFC 6749 bagian
    5.2](https://www.rfc-editor.org/rfc/rfc6749#section-5.2): `invalid_grant`,
    `invalid_client`, dan `mfa_required` dari Didit. Error layanan:
    `temporarily_unavailable`, `monitoring_unavailable`, `monitoring_disabled`.
    |

    | `{"<field>": ["<message>"]}` atau `{"<field>": "<code>"}` | Kolom
    permintaan gagal divalidasi. `non_field_errors` menunjukkan aturan lintas
    kolom, dan `unknown_field` menunjukkan kolom yang tidak diterima oleh
    endpoint. |

    | `{"detail": "<message>"}` | `401` (`invalid_token`), `403`
    (`origin_not_allowed`), `404` (aplikasi atau step-up tidak dikenal), dan
    `429` (batas laju). Juga `400` untuk body yang tidak dapat di-parse (`JSON
    parse error - ...`), `405` untuk metode yang tidak dilayani oleh endpoint,
    dan `415` untuk `Content-Type` yang tidak didukung. |


    Status `401` juga membawa `WWW-Authenticate: Bearer`, dan `429` membawa
    `Retry-After` dalam detik.


    Ada dua penolakan yang bukan berupa JSON. Alamat yang bukan merupakan
    penerbit akan ditolak sebelum mencapai endpoint ini, misalnya dengan `404`
    kosong untuk nama `*.auth.didit.me` yang tidak digunakan. `step_up_id` yang
    bukan merupakan UUID akan menghasilkan halaman `404`.


    ## Batas laju


    Sebuah permintaan harus melewati dua lapisan.


    **Batas endpoint**, dihitung per alamat IP klien, per aplikasi, dan per
    endpoint:


    - Discovery dan JWKS: 1,200 permintaan per menit dan 12,000 per jam.

    - Setiap endpoint lainnya: 30 permintaan per menit dan 300 per jam. `GET`
    dan `DELETE /session/` berbagi satu hitungan.


    Melebihi batas: `429 {"detail": "Too many requests from this IP address.
    Please wait before trying again. Expected available in 60 seconds."}`, atau
    `3600 seconds` untuk batas per jam.


    **Proteksi bot**, pada setiap `POST` dan `DELETE`. Bucket-nya digunakan
    bersama oleh semua permintaan `POST` dan `DELETE` aplikasi, termasuk langkah
    masuk yang di-hosting, dan diisi ulang terus-menerus:


    - 60 permintaan per menit per alamat IP klien.

    - 30 permintaan per menit per perangkat, untuk permintaan yang
    mengidentifikasi perangkatnya, seperti yang dilakukan oleh SDK browser dan
    native.

    - 3,000 permintaan per menit untuk seluruh aplikasi, di seluruh alamat IP.
    Pembaruan token (`POST /token/` dengan `grant_type=refresh_token`)
    menggunakan bucket tersendiri: 6,000 per menit untuk seluruh aplikasi.


    Melebihi bucket: `429 {"detail": "rate_limited Expected available in N
    seconds."}` (`1 second.` jika N bernilai 1). Bucket per IP mencakup seluruh
    endpoint, sehingga satu alamat yang mencampur panggilan token, step-up, dan
    keluar (sign-out) dapat mengosongkannya sebelum mencapai batas endpoint apa
    pun. Bucket aplikasi menghitung setiap klien dari aplikasi tersebut,
    termasuk browser.


    Kedua lapisan merespons sebelum endpoint dijalankan, sehingga permintaan
    yang mendapatkan `429` tidak dijalankan: coba lagi setelah `Retry-After`,
    termasuk pembaruan.


    ## Permintaan browser


    Permintaan yang membawa header `Origin` harus berasal dari salah satu origin
    milik aplikasi itu sendiri atau origin yang diizinkan, atau akan mendapatkan
    `403 origin_not_allowed`. Permintaan server-ke-server tidak mengirimkan
    `Origin`.


    ## Tidak dibahas di sini


    Rute-rute ini melayani halaman yang di-hosting, SDK browser dan native,
    serta agen AI. Rute ini bukan bagian dari kontrak server, dan SDK server
    tidak memanggilnya:


    - Langkah masuk: `POST /authorize/`, `POST /password/`, `/email/*`,
    `/phone/*`, `/password-reset/*`, `/passkeys/*`, `/mfa/*`, `/social/*`, dan
    `/protection/complete/`.

    - Halaman yang di-hosting dan callback: `/sign-in`, `/magic`, `/invite`,
    `/reset`, `/verify/`, `/sso/*`, `/wallet/callback/`, `/logo/`, serta file
    asosiasi aplikasi native di bawah `/.well-known/`.

    - Akses agen: `/agents/*` dan `/grants/`, termasuk `GET /agents/session/`,
    pemeriksaan live untuk token akses agen AI. Akses agen bukan bagian dari
    versi kontrak ini.

    - API manajemen konsol untuk Authentication, yang menggunakan sesi konsol.


    Event Authentication belum dikirimkan sebagai webhook, sehingga dokumen ini
    tidak mendefinisikan webhook apa pun.
servers:
  - url: https://{issuer}
    description: 'Penerbit aplikasi Anda: asal proses masuknya.'
    variables:
      issuer:
        default: your-app.auth.didit.me
        description: >-
          Host penerbit: `<name>.auth.didit.me` (produksi),
          `<name>.sandbox.auth.didit.me` (sandbox), atau domain kustom aktif
          Anda.
security: []
tags:
  - name: Issuer
    description: Kunci penemuan dan penandatanganan, untuk memverifikasi token.
  - name: Tokens
    description: Grant untuk kode otorisasi dan refresh.
  - name: Sessions
    description: Periksa dan akhiri sesi yang terkait dengan token akses.
  - name: Step-ups
    description: Verifikasi sebelum tindakan sensitif.
  - name: Monitoring
    description: Kredensial perangkat untuk event pemantauan transaksi.
paths:
  /token/:
    post:
      tags:
        - Tokens
      summary: Tukarkan kode atau perbarui token
      description: >-
        Endpoint token, untuk kedua jenis pemberian izin.


        - `authorization_code` menukarkan kode yang diterima `redirect_uri` Anda
        dengan token akses, token ID, dan token refresh. Kode ini berfungsi satu
        kali, dalam 60 detik, dengan verifier PKCE dan `redirect_uri` yang
        persis sama dengan proses masuknya. Ketika aplikasi mengizinkan satu
        sesi per orang, penukaran ini akan mengakhiri sesi lain milik orang
        tersebut.

        - `refresh_token` mengembalikan token akses, token ID, dan token refresh
        baru, serta token refresh yang Anda kirimkan akan berhenti berfungsi.
        **Mengirimkan kembali token refresh yang sudah digunakan akan mencabut
        sesi** dan mengembalikan `invalid_grant`, jadi jalankan satu penyegaran
        pada satu waktu untuk sebuah sesi dan ganti ketiga token tersebut secara
        bersamaan. Setelah penyegaran gagal dengan `400`, atau tidak mendapatkan
        respons, mulailah proses masuk kembali alih-alih mencoba lagi: mencoba
        lagi setelah penyegaran yang berhasil dianggap sebagai penggunaan ulang.
        Respons `429` dikirim sebelum proses penyegaran berjalan, jadi aman
        untuk mencoba lagi setelah `Retry-After`. Respons `503` berarti
        penyegaran tidak terjadi dan token refresh masih berfungsi: coba lagi
        dengan backoff.


        Isi dapat berupa JSON atau dikodekan dalam format formulir.
      operationId: authentication_token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
            examples:
              AuthorizationCode:
                summary: Tukarkan kode otorisasi
                value:
                  grant_type: authorization_code
                  code: Vb0bVt4CLlr8ZL0kE5mAxv8gS6R0rW3hYqC1pN7dJ2s
                  code_verifier: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
                  redirect_uri: https://app.example.com/auth/callback
              RefreshToken:
                summary: Segarkan token
                value:
                  grant_type: refresh_token
                  refresh_token: >-
                    N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/TokenRequest'
      responses:
        '200':
          description: Token baru untuk sesi tersebut.
          headers:
            Cache-Control:
              description: >-
                Selalu `no-store`: jangan pernah menyimpan respons ini dalam
                cache.
              schema:
                type: string
                example: no-store
            Pragma:
              description: Selalu `no-cache`.
              schema:
                type: string
                example: no-cache
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
              examples:
                Tokens:
                  summary: Token untuk sesi baru
                  value:
                    access_token: >-
                      eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6ImF0K2p3dCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJSZ3F1NzE3OWUtbGhKMGJ2VmVELVlnIiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJjbGllbnRfaWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQifQ.AxrqN8Pm-Op-3OayZk8HcvlFQZhozRiOvQ0SgQsGgrz4UNeJlUEeSlq9B8WcYUuM8naRaY5kQt_TUofnsYcyYQ
                    id_token: >-
                      eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJNTHRPNHM4SHItQnJrcDd2NTlPV0Z3IiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJhdXRoX3RpbWUiOjE3OTIyMzM2NzQsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJlbWFpbCI6InBlcnNvbkBleGFtcGxlLmNvbSIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.avhfDFp_aUV5_1ssYzwDcIA7H6Waf19P0bn5GCg25FhLIZBGIlwc8HiUPGj1j26r21OkP4FQnS_jM1Ho3dHtKQ
                    refresh_token: >-
                      N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
                    token_type: Bearer
                    expires_in: 299
                    session_id: c9abf6cc-2d64-4bf2-b31e-a4d09a070ab1
        '400':
          description: >-
            Grant gagal. `invalid_grant`: kode atau refresh token tidak dikenal,
            sudah digunakan, atau kedaluwarsa; pemverifikasi PKCE atau
            `redirect_uri` tidak cocok, atau `redirect_uri` sudah tidak
            terdaftar lagi; orang tersebut diblokir atau emailnya sudah tidak
            terverifikasi lagi; sesi telah berakhir atau tidak lagi memenuhi
            kebijakan MFA Anda; atau, untuk kode, Anda menonaktifkan metode
            masuk asalnya. Menonaktifkan suatu metode tidak menghentikan
            pembaruan sesi yang sudah dimulainya. `invalid_client`: `client_id`
            bukan ID aplikasi Anda. `mfa_required`: proses masuk tidak memenuhi
            kebijakan MFA Anda. Kesalahan kolom menyebutkan kolom yang hilang
            atau tidak valid. Body yang tidak dapat di-parse akan menerima pesan
            `detail`.
          headers:
            Cache-Control:
              description: >-
                Selalu `no-store`: jangan pernah menyimpan respons ini dalam
                cache.
              schema:
                type: string
                example: no-store
            Pragma:
              description: Selalu `no-cache`.
              schema:
                type: string
                example: no-cache
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/OAuthError'
                  - $ref: '#/components/schemas/FieldErrors'
                  - $ref: '#/components/schemas/Detail'
              examples:
                InvalidGrant:
                  summary: Kode atau token penyegaran ditolak
                  value:
                    error: invalid_grant
                InvalidClient:
                  summary: client_id salah
                  value:
                    error: invalid_client
                MfaRequired:
                  summary: Kebijakan MFA tidak terpenuhi
                  value:
                    error: mfa_required
                MissingFields:
                  summary: Pemberian izin tanpa kolom wajibnya
                  value:
                    non_field_errors:
                      - invalid_request
                UnknownGrant:
                  summary: grant_type tidak didukung
                  value:
                    grant_type:
                      - '"password" is not a valid choice.'
                UnknownField:
                  summary: Kolom yang tidak diterima oleh endpoint
                  value:
                    foo: unknown_field
                MalformedBody:
                  summary: Body JSON yang tidak dapat diurai
                  value:
                    detail: >-
                      JSON parse error - Expecting property name enclosed in
                      double quotes: line 1 column 2 (char 1)
        '403':
          $ref: '#/components/responses/OriginNotAllowed'
        '404':
          $ref: '#/components/responses/ApplicationNotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/TemporarilyUnavailable'
      security:
        - {}
components:
  schemas:
    TokenRequest:
      description: >-
        Salah satu dari dua grant, dipilih oleh `grant_type`. Endpoint menerima
        kolom dari kedua grant dan memvalidasi setiap kolom yang diterimanya,
        sehingga refresh yang juga membawa `code_verifier` dengan format salah
        akan mendapatkan error kolom. Kirimkan hanya kolom grant Anda; kolom
        lain akan mendapatkan `unknown_field`.
      oneOf:
        - $ref: '#/components/schemas/AuthorizationCodeGrantRequest'
        - $ref: '#/components/schemas/RefreshTokenGrantRequest'
      discriminator:
        propertyName: grant_type
        mapping:
          authorization_code: '#/components/schemas/AuthorizationCodeGrantRequest'
          refresh_token: '#/components/schemas/RefreshTokenGrantRequest'
    TokenResponse:
      type: object
      description: >-
        Token untuk satu sesi. Ganti ketiganya secara bersamaan setelah
        penyegaran.
      required:
        - access_token
        - id_token
        - refresh_token
        - token_type
        - expires_in
        - session_id
      properties:
        access_token:
          type: string
          description: >-
            JWT ES256, header `typ: at+jwt`. Kirimkan sebagai `Authorization:
            Bearer <access_token>`. Klaim: `AccessTokenClaims`.
          example: >-
            eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6ImF0K2p3dCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJSZ3F1NzE3OWUtbGhKMGJ2VmVELVlnIiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJjbGllbnRfaWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQifQ.AxrqN8Pm-Op-3OayZk8HcvlFQZhozRiOvQ0SgQsGgrz4UNeJlUEeSlq9B8WcYUuM8naRaY5kQt_TUofnsYcyYQ
          x-didit-jwt:
            header: '#/components/schemas/AccessTokenHeader'
            claims: '#/components/schemas/AccessTokenClaims'
        id_token:
          type: string
          description: >-
            JWT ES256, header `typ: JWT`, agar aplikasi Anda membaca siapa yang
            masuk. Jangan pernah mengirimkannya sebagai token akses. Klaim:
            `IDTokenClaims`.
          example: >-
            eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJNTHRPNHM4SHItQnJrcDd2NTlPV0Z3IiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJhdXRoX3RpbWUiOjE3OTIyMzM2NzQsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJlbWFpbCI6InBlcnNvbkBleGFtcGxlLmNvbSIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.avhfDFp_aUV5_1ssYzwDcIA7H6Waf19P0bn5GCg25FhLIZBGIlwc8HiUPGj1j26r21OkP4FQnS_jM1Ho3dHtKQ
          x-didit-jwt:
            header: '#/components/schemas/IDTokenHeader'
            claims: '#/components/schemas/IDTokenClaims'
        refresh_token:
          type: string
          description: >-
            Sekali pakai. Berlaku sampai sesi berakhir, dan setiap refresh
            mengembalikan URL baru.
          example: N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
        token_type:
          type: string
          enum:
            - Bearer
        expires_in:
          type: integer
          minimum: 0
          maximum: 300
          description: >-
            Detik hingga token akses kedaluwarsa: paling banyak 300, lebih
            sedikit jika sesi berakhir lebih cepat.
          example: 299
        session_id:
          type: string
          format: uuid
          description: >-
            Sesi tempat token-token ini berada, nilai yang sama dengan klaim
            `sid`.
          example: c9abf6cc-2d64-4bf2-b31e-a4d09a070ab1
    OAuthError:
      title: OAuth error
      type: object
      description: Error OAuth (RFC 6749 bagian 5.2) atau error layanan.
      required:
        - error
      properties:
        error:
          type: string
          example: invalid_grant
    FieldErrors:
      title: Field errors
      type: object
      description: >-
        Kesalahan validasi berdasarkan kolom. Nilai berupa daftar pesan, atau
        satu kode tunggal (`unknown_field`, `invalid_redirect_uri`).
        `non_field_errors` menampung aturan lintas kolom.
      additionalProperties:
        oneOf:
          - type: string
          - type: array
            items:
              type: string
    Detail:
      type: object
      required:
        - detail
      properties:
        detail:
          type: string
          example: invalid_token
    AuthorizationCodeGrantRequest:
      title: Authorization code grant
      type: object
      description: >-
        Tukarkan kode otorisasi dari pengalihan Anda dengan token. Kode
        berfungsi sekali, selama 60 detik.
      required:
        - grant_type
        - code
        - code_verifier
        - redirect_uri
      properties:
        grant_type:
          type: string
          enum:
            - authorization_code
        code:
          type: string
          maxLength: 128
          description: >-
            Parameter kueri `code` yang diterima `redirect_uri` Anda. Periksa
            `state` callback sebelum Anda menukarnya.
          example: Vb0bVt4CLlr8ZL0kE5mAxv8gS6R0rW3hYqC1pN7dJ2s
        code_verifier:
          type: string
          minLength: 43
          maxLength: 128
          pattern: ^[A-Za-z0-9._~-]{43,128}$
          description: >-
            Verifier PKCE yang hash S256-nya merupakan `code_challenge` proses
            masuk.
          example: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
        redirect_uri:
          type: string
          maxLength: 2048
          description: >-
            `redirect_uri` persis yang digunakan saat proses masuk dimulai.
            Nilai ini harus tetap merupakan salah satu URI pengalihan aplikasi.
          example: https://app.example.com/auth/callback
        client_id:
          type: string
          maxLength: 64
          description: >-
            Opsional. Jika dikirim, nilainya harus sama dengan ID aplikasi Anda,
            atau permintaan akan gagal dengan `invalid_client`.
          example: 7b0a6c2e-3f41-4c9a-8d55-1e2f3a4b5c6d
        scope:
          type: string
          maxLength: 512
          description: >-
            Opsional dan diterima dari klien OpenID Connect standar. Ini tidak
            pernah memperluas izin yang diberikan saat masuk.
          example: openid email
    RefreshTokenGrantRequest:
      title: Refresh token grant
      type: object
      description: >-
        Tukarkan refresh token dengan access, ID, dan refresh token baru.
        Refresh token hanya berfungsi satu kali: mengirimkannya lagi akan
        mencabut sesi, jadi jalankan satu pembaruan pada satu waktu untuk suatu
        sesi.
      required:
        - grant_type
        - refresh_token
      properties:
        grant_type:
          type: string
          enum:
            - refresh_token
        refresh_token:
          type: string
          maxLength: 128
          description: '`refresh_token` dari respons token sebelumnya.'
          example: N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
        client_id:
          type: string
          maxLength: 64
          description: >-
            Opsional. Jika dikirim, nilainya harus sama dengan ID aplikasi Anda,
            atau permintaan akan gagal dengan `invalid_client`.
          example: 7b0a6c2e-3f41-4c9a-8d55-1e2f3a4b5c6d
        scope:
          type: string
          maxLength: 512
          description: >-
            Opsional dan diterima dari klien OpenID Connect standar. Refresh
            tidak pernah memperluas apa yang dimiliki sesi.
          example: openid email
  responses:
    OriginNotAllowed:
      description: >-
        Permintaan membawa `Origin` yang bukan milik aplikasi dan tidak terdapat
        dalam daftar origin yang diizinkan. Permintaan server tidak mengirimkan
        `Origin`.
      headers:
        Cache-Control:
          description: 'Selalu `no-store`: jangan pernah menyimpan respons ini dalam cache.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Selalu `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            OriginNotAllowed:
              summary: Asal tidak diizinkan
              value:
                detail: origin_not_allowed
    ApplicationNotFound:
      description: >-
        Alamat milik suatu aplikasi, tetapi Autentikasi tidak aktif untuk
        aplikasi tersebut; pada alamat `apx.didit.me`, ID aplikasi tidak
        diketahui. Alamat yang bukan milik aplikasi mana pun ditolak sebelum
        mencapai endpoint, dan respons tersebut bukan JSON.
      headers:
        Cache-Control:
          description: 'Selalu `no-store`: jangan pernah menyimpan respons ini dalam cache.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Selalu `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            ApplicationNotFound:
              summary: Aplikasi tidak dikenal
              value:
                detail: No Configuration matches the given query.
    RateLimited:
      description: >-
        Terlalu banyak permintaan dari alamat ini untuk endpoint ini, atau, pada
        `POST` atau `DELETE`, bucket perlindungan bot kosong. Tidak ada yang
        dijalankan: tunggu selama `Retry-After` detik, lalu coba lagi.
      headers:
        Retry-After:
          description: Detik untuk menunggu sebelum mencoba lagi.
          schema:
            type: integer
            example: 60
        Cache-Control:
          description: 'Selalu `no-store`: jangan pernah menyimpan respons ini dalam cache.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Selalu `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            RateLimited:
              summary: Batas endpoint
              value:
                detail: >-
                  Too many requests from this IP address. Please wait before
                  trying again. Expected available in 60 seconds.
            BotProtection:
              summary: Batas perlindungan bot (POST dan DELETE)
              value:
                detail: rate_limited Expected available in 2 seconds.
    TemporarilyUnavailable:
      description: >-
        Layanan yang dibutuhkan permintaan sedang tidak tersedia untuk
        sementara. Coba lagi dengan backoff.
      headers:
        Cache-Control:
          description: 'Selalu `no-store`: jangan pernah menyimpan respons ini dalam cache.'
          schema:
            type: string
            example: no-store
        Pragma:
          description: Selalu `no-cache`.
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OAuthError'
          examples:
            TemporarilyUnavailable:
              summary: Coba lagi nanti
              value:
                error: temporarily_unavailable

````

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