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

# Report key usage

> Records requests made with API keys your signup gate issued to your accounts, one event per request. Each event names the key by its SHA-256, the `issued_credential.sha256` the account's assessment carried, and gives the client `ip`, `occurred_at` and optionally `user_agent`, `model`, `input_tokens` and `output_tokens`. The Node and Python SDKs buffer events and send them in the background.

A batch holds 1 to 500 events and at most 256 KiB (413 above that). An event whose IP is not a public address, or whose `occurred_at` is more than 24 hours old or more than 5 minutes ahead, is counted in `ignored`. Sampling is allowed: `sample_rate` weights an event's request and token counts.

NoResponse keeps no raw event. Request and token totals are kept per key for 30 days after the key's last use, or your retention when that is shorter. The IP, user agent and model are kept only as keyed digests, the IP with its country, for 24 hours. When one key is used from 20 or more IP addresses, or from 3 or more countries, within 24 hours, every account whose assessment carried that key gets the `shared_api_key` reason in its current risk for 30 days.

`Idempotency-Key` is required: a retry with the same key and body answers the same record with `Idempotent-Replayed: true` and counts nothing again, and the same key with a different body answers 409. Requires the `assess` scope.



## OpenAPI

````yaml /openapi.json post /v1/key_usage/batches
openapi: 3.1.0
info:
  title: NoResponse API
  version: '1'
  description: >-
    The NoResponse API assesses a signup, login or purchase and answers with the
    account's one current risk: a 0 to 100 score, a decision for the flow
    (`allow`, `step_up`, `review` or `block`), the reasons behind it and the
    decision for each flow. The assessment's own facts, such as its status,
    confidence and incomplete sources, sit under `detail`.


    Authenticate every request with your API key as `Authorization: Bearer
    nrk_...`. Call the API only from your backend.


    Guides, SDKs and webhook verification are at
    [docs.noresponse.org](https://docs.noresponse.org/). Every error is an RFC
    9457 problem document; see
    [Errors](https://docs.noresponse.org/reference/errors).
servers:
  - url: https://api.noresponse.org
    description: Production
security:
  - tenantBearer: []
tags:
  - name: assess
    x-displayName: Assessments
    x-group: Assessments
    description: Create, read and list assessments, and report outcomes.
  - name: accounts
    x-displayName: Accounts
    x-group: Accounts
    description: >-
      Each of your accounts holds one current risk, and its assessments are its
      history.
  - name: usage
    x-displayName: Key usage
    x-group: Key usage
    description: >-
      Report usage of the API keys your signup gate issued, so a shared or
      stolen key raises its account's risk.
  - name: keys
    x-displayName: API keys
    x-group: API keys
    description: Rotate your own API key.
  - name: integrations
    x-displayName: Integrations
    x-group: Integrations
    description: >-
      Hosted endpoints that answer your WorkOS or Auth0 sign-up and sign-in
      hooks.
  - name: webhooks
    x-displayName: Webhooks
    x-group: Webhooks
    description: Your webhook endpoints, and the signed events NoResponse sends to them.
paths:
  /v1/key_usage/batches:
    post:
      tags:
        - usage
      summary: Report key usage
      description: >-
        Records requests made with API keys your signup gate issued to your
        accounts, one event per request. Each event names the key by its
        SHA-256, the `issued_credential.sha256` the account's assessment
        carried, and gives the client `ip`, `occurred_at` and optionally
        `user_agent`, `model`, `input_tokens` and `output_tokens`. The Node and
        Python SDKs buffer events and send them in the background.


        A batch holds 1 to 500 events and at most 256 KiB (413 above that). An
        event whose IP is not a public address, or whose `occurred_at` is more
        than 24 hours old or more than 5 minutes ahead, is counted in `ignored`.
        Sampling is allowed: `sample_rate` weights an event's request and token
        counts.


        NoResponse keeps no raw event. Request and token totals are kept per key
        for 30 days after the key's last use, or your retention when that is
        shorter. The IP, user agent and model are kept only as keyed digests,
        the IP with its country, for 24 hours. When one key is used from 20 or
        more IP addresses, or from 3 or more countries, within 24 hours, every
        account whose assessment carried that key gets the `shared_api_key`
        reason in its current risk for 30 days.


        `Idempotency-Key` is required: a retry with the same key and body
        answers the same record with `Idempotent-Replayed: true` and counts
        nothing again, and the same key with a different body answers 409.
        Requires the `assess` scope.
      operationId: recordKeyUsage
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KeyUsageBatchRequest'
            examples:
              batch:
                value:
                  events:
                    - key_sha256: >-
                        9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
                      ip: 203.0.113.24
                      occurred_at: '2026-10-23T09:14:07Z'
                      user_agent: python-requests/2.32.3
                      model: gpt-4o-mini
                      input_tokens: 812
                      output_tokens: 164
                    - key_sha256: >-
                        9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
                      ip: 2001:db8:4a::17
                      occurred_at: '2026-10-23T09:14:09.250Z'
                      sample_rate: 0.25
      responses:
        '202':
          $ref: '#/components/responses/KeyUsageRecorded'
        '400':
          $ref: '#/components/responses/InvalidKeyUsage'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/WorkspaceForbidden'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '413':
          $ref: '#/components/responses/KeyUsageTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
      security:
        - tenantBearer: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl --request POST \
              --url 'https://api.noresponse.org/v1/key_usage/batches' \
              --header "Authorization: Bearer $NORESPONSE_API_KEY" \
              --header 'Content-Type: application/json' \
              --header 'Idempotency-Key: account-acct_7f3a9c' \
              --data '{
              "events": [
                {
                  "key_sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                  "ip": "203.0.113.24",
                  "occurred_at": "2026-10-23T09:14:07Z",
                  "user_agent": "python-requests/2.32.3",
                  "model": "gpt-4o-mini",
                  "input_tokens": 812,
                  "output_tokens": 164,
                  "sample_rate": 1
                },
                {
                  "key_sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                  "ip": "2001:db8:4a::17",
                  "occurred_at": "2026-10-23T09:14:09.250Z",
                  "sample_rate": 0.25
                }
              ]
            }'
        - lang: typescript
          label: TypeScript
          source: |
            import { NoResponse } from "@noresponse/node";

            const noResponse = new NoResponse({
              apiKey: process.env["NORESPONSE_API_KEY"] ?? "",
            });

            async function run() {
              const result = await noResponse.keyUsage.record({
                idempotencyKey: "account-acct_7f3a9c",
                body: {
                  events: [
                    {
                      keySha256: "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                      ip: "203.0.113.24",
                      occurredAt: "2026-10-23T09:14:07Z",
                      userAgent: "python-requests/2.32.3",
                      model: "gpt-4o-mini",
                      inputTokens: 812,
                      outputTokens: 164,
                    },
                    {
                      keySha256: "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                      ip: "2001:db8:4a::17",
                      occurredAt: "2026-10-23T09:14:09.250Z",
                      sampleRate: 0.25,
                    },
                  ],
                },
              });

              console.log(result);
            }

            run();
        - lang: python
          label: Python
          source: |
            from noresponse import NoResponse
            import os


            with NoResponse(
                api_key=os.getenv("NORESPONSE_API_KEY", ""),
            ) as no_response:

                res = no_response.key_usage.record(idempotency_key="account-acct_7f3a9c", events=[
                    {
                        "key_sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                        "ip": "203.0.113.24",
                        "occurred_at": "2026-10-23T09:14:07Z",
                        "user_agent": "python-requests/2.32.3",
                        "model": "gpt-4o-mini",
                        "input_tokens": 812,
                        "output_tokens": 164,
                    },
                    {
                        "key_sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                        "ip": "2001:db8:4a::17",
                        "occurred_at": "2026-10-23T09:14:09.250Z",
                        "sample_rate": 0.25,
                    },
                ])

                # Handle response
                print(res)
        - lang: go
          label: Go
          source: "package main\n\nimport(\n\t\"context\"\n\t\"os\"\n\tnoresponse \"github.com/noresponsehq/go/v5\"\n\t\"github.com/noresponsehq/go/v5/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n    ctx := context.Background()\n\n    s := noresponse.New(\n        noresponse.WithSecurity(os.Getenv(\"NORESPONSE_API_KEY\")),\n    )\n\n    res, err := s.KeyUsage.Record(ctx, \"account-acct_7f3a9c\", components.KeyUsageBatchRequest{\n        Events: []components.KeyUsageEvent{\n            components.KeyUsageEvent{\n                KeySha256: \"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\",\n                IP: \"203.0.113.24\",\n                OccurredAt: \"2026-10-23T09:14:07Z\",\n                UserAgent: noresponse.Pointer(\"python-requests/2.32.3\"),\n                Model: noresponse.Pointer(\"gpt-4o-mini\"),\n                InputTokens: noresponse.Pointer[int64](812),\n                OutputTokens: noresponse.Pointer[int64](164),\n            },\n            components.KeyUsageEvent{\n                KeySha256: \"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\",\n                IP: \"2001:db8:4a::17\",\n                OccurredAt: \"2026-10-23T09:14:09.250Z\",\n                SampleRate: noresponse.Pointer[float64](0.25),\n            },\n        },\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n    if res != nil {\n        // handle response\n    }\n}\n"
        - lang: java
          label: Java
          source: |
            package hello.world;

            import java.lang.Exception;
            import java.util.List;
            import org.noresponse.NoResponse;
            import org.noresponse.models.errors.AuthenticationException;
            import org.noresponse.models.errors.BadRequestException;
            import org.noresponse.models.errors.ConflictException;
            import org.noresponse.models.errors.NotFoundException;
            import org.noresponse.models.errors.PermissionDeniedException;
            import org.noresponse.models.errors.RateLimitException;
            import org.noresponse.models.operations.RecordKeyUsageResponse;
            import org.noresponse.models.shared.KeyUsageBatchRequest;
            import org.noresponse.models.shared.KeyUsageEvent;

            public class Application {

                public static void main(String[] args) throws Exception {

                    NoResponse sdk = NoResponse.builder()
                            .apiKey(System.getenv().getOrDefault("NORESPONSE_API_KEY", ""))
                        .build();

                    RecordKeyUsageResponse res = sdk.keyUsage().record()
                            .idempotencyKey("account-acct_7f3a9c")
                            .body(KeyUsageBatchRequest.builder()
                                .events(List.of(
                                    KeyUsageEvent.builder()
                                        .keySha256("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")
                                        .ip("203.0.113.24")
                                        .occurredAt("2026-10-23T09:14:07Z")
                                        .userAgent("python-requests/2.32.3")
                                        .model("gpt-4o-mini")
                                        .inputTokens(812L)
                                        .outputTokens(164L)
                                        .build(),
                                    KeyUsageEvent.builder()
                                        .keySha256("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")
                                        .ip("2001:db8:4a::17")
                                        .occurredAt("2026-10-23T09:14:09.250Z")
                                        .sampleRate(0.25)
                                        .build()))
                                .build())
                            .call();

                    if (res.keyUsageBatch().isPresent()) {
                        System.out.println(res.keyUsageBatch().get());
                    }
                }
            }
        - lang: csharp
          label: C#
          source: >
            using NoResponse;

            using NoResponse.Models.Shared;

            using System;

            using System.Collections.Generic;


            var sdk = new NoResponseClient(apiKey:
            Environment.GetEnvironmentVariable("NORESPONSE_API_KEY"));


            var res = await sdk.KeyUsage.RecordAsync(
                body: new KeyUsageBatchRequest() {
                    Events = new List<KeyUsageEvent>() {
                        new KeyUsageEvent() {
                            KeySha256 = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                            Ip = "203.0.113.24",
                            OccurredAt = "2026-10-23T09:14:07Z",
                            UserAgent = "python-requests/2.32.3",
                            Model = "gpt-4o-mini",
                            InputTokens = 812,
                            OutputTokens = 164,
                        },
                        new KeyUsageEvent() {
                            KeySha256 = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                            Ip = "2001:db8:4a::17",
                            OccurredAt = "2026-10-23T09:14:09.250Z",
                            SampleRate = 0.25D,
                        },
                    },
                },
                idempotencyKey: "account-acct_7f3a9c"
            );


            // handle response
        - lang: php
          label: PHP
          source: |
            <?php

            declare(strict_types=1);

            require 'vendor/autoload.php';

            use NoResponse;
            use NoResponse\Models\Shared;

            $sdk = NoResponse\NoResponseClient::builder()
                ->setSecurity(
                    getenv('NORESPONSE_API_KEY') ?: ''
                )
                ->build();

            $body = new Shared\KeyUsageBatchRequest(
                events: [
                    new Shared\KeyUsageEvent(
                        keySha256: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08',
                        ip: '203.0.113.24',
                        occurredAt: '2026-10-23T09:14:07Z',
                        userAgent: 'python-requests/2.32.3',
                        model: 'gpt-4o-mini',
                        inputTokens: 812,
                        outputTokens: 164,
                    ),
                    new Shared\KeyUsageEvent(
                        keySha256: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08',
                        ip: '2001:db8:4a::17',
                        occurredAt: '2026-10-23T09:14:09.250Z',
                        sampleRate: 0.25,
                    ),
                ],
            );

            $response = $sdk->keyUsage->record(
                body: $body,
                idempotencyKey: 'account-acct_7f3a9c'

            );

            if ($response->keyUsageBatch !== null) {
                // handle response
            }
        - lang: ruby
          label: Ruby
          source: >
            require 'noresponse'


            Models = ::NoResponse::Models

            s = ::NoResponse::NoResponseClient.new(
              api_key: ENV.fetch('NORESPONSE_API_KEY', '')
            )

            res = s.key_usage.record(body:
            Models::Shared::KeyUsageBatchRequest.new(
              events: [
                Models::Shared::KeyUsageEvent.new(
                  key_sha256: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08',
                  ip: '203.0.113.24',
                  occurred_at: '2026-10-23T09:14:07Z',
                  user_agent: 'python-requests/2.32.3',
                  model: 'gpt-4o-mini',
                  input_tokens: 812,
                  output_tokens: 164
                ),
                Models::Shared::KeyUsageEvent.new(
                  key_sha256: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08',
                  ip: '2001:db8:4a::17',
                  occurred_at: '2026-10-23T09:14:09.250Z',
                  sample_rate: 0.25
                ),
              ]
            ), idempotency_key: 'account-acct_7f3a9c')


            unless res.nil?
              # handle response
            end
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: >-
        Opaque client key, 16 to 128 characters of [A-Za-z0-9._:-] (a UUID or
        ULID fits). Scoped to the workspace principal and bound to the canonical
        body for 24 hours.
      schema:
        type: string
        minLength: 16
        maxLength: 128
        pattern: ^[A-Za-z0-9._:-]{16,128}$
        not:
          pattern: \n
  schemas:
    KeyUsageBatchRequest:
      title: Key usage batch
      description: >-
        POST /v1/key_usage/batches body: facts about requests made with API keys
        your platform issued to your accounts, one event per request. Each event
        names the key by the SHA-256 that the assessment's
        issued_credential.sha256 carried, not the key itself. The SDKs buffer
        events and send them in the background. A batch holds 1 to 500 events
        and at most 256 KiB. Idempotency-Key is required and binds the body as
        on POST /v1/assessments, so a retried batch is counted once.
      type: object
      additionalProperties: false
      required:
        - events
      properties:
        events:
          type: array
          minItems: 1
          maxItems: 500
          items:
            type: object
            additionalProperties: false
            required:
              - key_sha256
              - ip
              - occurred_at
            description: >-
              One request made with an issued key. An event whose ip is not a
              public address, or whose occurred_at is more than 24 hours old or
              more than 5 minutes ahead, is counted in ignored and adds nothing.
            properties:
              key_sha256:
                type: string
                pattern: ^[0-9a-f]{64}$
                description: >-
                  SHA-256 of the issued key's UTF-8 bytes, as 64 lower-case hex
                  characters: the value sent as issued_credential.sha256 when
                  the account was assessed. Each SDK's issued-credential helper
                  computes it.
              ip:
                type: string
                minLength: 2
                maxLength: 64
                description: >-
                  The client IP address the request came from, IPv4 or IPv6, as
                  your server saw it. Stored only as a keyed digest and its
                  country.
                not:
                  description: >-
                    A line feed anywhere, refused with `not` beside every
                    anchored pattern.
                  pattern: \n
              occurred_at:
                type: string
                format: date-time
                description: When the request was made.
              user_agent:
                type: string
                minLength: 1
                maxLength: 512
                description: >-
                  The request's User-Agent header, at most 512 characters.
                  Stored only as a keyed digest.
                not:
                  pattern: '[\x00-\x1f\x7f]'
              model:
                type: string
                minLength: 1
                maxLength: 128
                pattern: ^[A-Za-z0-9][A-Za-z0-9._:/@+-]{0,127}$
                description: >-
                  The model or product the request used, such as gpt-4o-mini.
                  Stored only as a keyed digest.
                not:
                  description: >-
                    A line feed anywhere, refused with `not` beside every
                    anchored pattern.
                  pattern: \n
              input_tokens:
                type: integer
                minimum: 0
                maximum: 1000000000
                description: Input tokens the request consumed.
              output_tokens:
                type: integer
                minimum: 0
                maximum: 1000000000
                description: Output tokens the request produced.
              sample_rate:
                type: number
                exclusiveMinimum: 0
                maximum: 1
                default: 1
                description: >-
                  The share of requests your sampler records, when it records
                  only some: 0.1 means this event stands for 10 requests.
                  Request and token totals are weighted by its inverse. Distinct
                  IP and country counts see only the events sent.
      $defs:
        key_usage_event:
          type: object
          additionalProperties: false
          required:
            - key_sha256
            - ip
            - occurred_at
          description: >-
            One request made with an issued key. An event whose ip is not a
            public address, or whose occurred_at is more than 24 hours old or
            more than 5 minutes ahead, is counted in ignored and adds nothing.
          properties:
            key_sha256:
              type: string
              pattern: ^[0-9a-f]{64}$
              description: >-
                SHA-256 of the issued key's UTF-8 bytes, as 64 lower-case hex
                characters: the value sent as issued_credential.sha256 when the
                account was assessed. Each SDK's issued-credential helper
                computes it.
            ip:
              type: string
              minLength: 2
              maxLength: 64
              description: >-
                The client IP address the request came from, IPv4 or IPv6, as
                your server saw it. Stored only as a keyed digest and its
                country.
              not:
                description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
            occurred_at:
              type: string
              format: date-time
              description: When the request was made.
            user_agent:
              type: string
              minLength: 1
              maxLength: 512
              description: >-
                The request's User-Agent header, at most 512 characters. Stored
                only as a keyed digest.
              not:
                pattern: '[\x00-\x1f\x7f]'
            model:
              type: string
              minLength: 1
              maxLength: 128
              pattern: ^[A-Za-z0-9][A-Za-z0-9._:/@+-]{0,127}$
              description: >-
                The model or product the request used, such as gpt-4o-mini.
                Stored only as a keyed digest.
              not:
                description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
            input_tokens:
              type: integer
              minimum: 0
              maximum: 1000000000
              description: Input tokens the request consumed.
            output_tokens:
              type: integer
              minimum: 0
              maximum: 1000000000
              description: Output tokens the request produced.
            sample_rate:
              type: number
              exclusiveMinimum: 0
              maximum: 1
              default: 1
              description: >-
                The share of requests your sampler records, when it records only
                some: 0.1 means this event stands for 10 requests. Request and
                token totals are weighted by its inverse. Distinct IP and
                country counts see only the events sent.
    KeyUsageBatch:
      title: Recorded key usage batch
      description: >-
        The 202 answer of POST /v1/key_usage/batches. A replay with the same
        Idempotency-Key and body answers the same record and counts nothing
        again.
      type: object
      additionalProperties: false
      required:
        - id
        - received_at
        - accepted
        - ignored
      properties:
        id:
          type: string
          pattern: ^kub_[0-9A-HJKMNP-TV-Z]{26}$
          description: >-
            Opaque, server-assigned id of the batch: 'kub_' and a 26-character
            Crockford base32 ULID.
          not:
            description: >-
              A line feed anywhere, refused with `not` beside every anchored
              pattern.
            pattern: \n
        received_at:
          type: string
          format: date-time
          description: When NoResponse first recorded the batch.
        accepted:
          type: integer
          minimum: 0
          maximum: 500
          description: Events counted.
        ignored:
          type: integer
          minimum: 0
          maximum: 500
          description: >-
            Events that added nothing: a private, reserved or unreadable ip, or
            an occurred_at more than 24 hours old or more than 5 minutes ahead.
    Problem:
      title: Problem details error
      description: >-
        RFC 9457 problem-details error body returned by every non-2xx API
        response, extended with a stable machine code and request id.
      type: object
      additionalProperties: false
      required:
        - type
        - title
        - status
        - detail
        - code
        - request_id
      properties:
        type:
          type: string
          format: uri-reference
        title:
          type: string
        status:
          type: integer
          minimum: 400
          maximum: 599
        detail:
          type: string
        instance:
          type: string
          format: uri-reference
        code:
          type: string
        request_id:
          type: string
          minLength: 1
        invalid_params:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - field
              - reason
            properties:
              field:
                type: string
                minLength: 1
              reason:
                type: string
                minLength: 1
              example:
                type: string
                minLength: 1
  responses:
    KeyUsageRecorded:
      description: >-
        The batch was recorded: accepted events are counted and ignored ones
        added nothing. A replay of the same Idempotency-Key and body answers the
        record the first request created, with Idempotent-Replayed: true, and
        counts nothing again.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        Idempotent-Replayed:
          description: >-
            Present and true when this answer replays an earlier request with
            the same Idempotency-Key and body: the body is the record that
            request created.
          schema:
            type: string
            const: 'true'
          required: false
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/KeyUsageBatch'
          examples:
            recorded:
              value:
                id: kub_01JA2ZE4M5N6P7Q8R9S0T1V2W3
                received_at: '2026-10-23T09:14:12Z'
                accepted: 2
                ignored: 0
    InvalidKeyUsage:
      description: >-
        The body is not JSON or does not match the key usage batch request
        schema (no events or more than 500, an unknown member such as tenant_id,
        a key_sha256 that is not 64 lower-case hex, an over-long user_agent, a
        sample_rate outside (0, 1]), or Idempotency-Key is missing or malformed.
        invalid_params names the offending fields, never their values.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 400
                  code:
                    enum:
                      - invalid_request
                      - invalid_idempotency_key
    Unauthorized:
      description: No API key, or one that is malformed, unknown, revoked or expired.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        WWW-Authenticate:
          schema:
            type: string
            const: Bearer realm="noresponse-assessments"
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 401
                  code:
                    enum:
                      - unauthorized
    WorkspaceForbidden:
      description: >-
        The API key is valid but may not use this operation: it lacks the
        operation's scope, or its workspace is not entitled or is suspended.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 403
                  code:
                    enum:
                      - workspace_forbidden
          examples:
            missingScope:
              value:
                type: https://docs.noresponse.org/problems/workspace-forbidden
                title: Forbidden
                status: 403
                detail: The API key does not carry a scope this operation accepts.
                code: workspace_forbidden
                request_id: 0123456789abcdef0123456789abcdef
    IdempotencyConflict:
      description: >-
        The Idempotency-Key was already used by this workspace, within 24 hours,
        with a different body. A reuse with the same body is never a conflict,
        even while the first request is in flight: it replays the latest
        assessment.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 409
                  code:
                    enum:
                      - idempotency_key_reused
          examples:
            reused:
              value:
                type: https://docs.noresponse.org/problems/idempotency-key-reused
                title: Idempotency key reused
                status: 409
                detail: This Idempotency-Key was used with a different request body.
                code: idempotency_key_reused
                request_id: 0123456789abcdef0123456789abcdef
    KeyUsageTooLarge:
      description: >-
        The batch body exceeds 262144 bytes (256 KiB). Refused before parsing:
        split the batch.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 413
                  code:
                    enum:
                      - payload_too_large
    UnsupportedMediaType:
      description: Content-Type is not application/json.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 415
                  code:
                    enum:
                      - unsupported_media_type
    RateLimited:
      description: >-
        The workspace's rate or concurrency limit is exhausted. Retry-After says
        when to retry; the provider applies its own fallback meanwhile.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        Retry-After:
          description: >-
            Whole seconds to wait before polling or retrying. Required on 202,
            429 and 503.
          schema:
            type: string
            pattern: >-
              ^(?:[1-9]|[1-9][0-9]|[1-9][0-9]{2}|[12][0-9]{3}|3[0-5][0-9]{2}|3600)$
            description: Whole decimal seconds; wait at least this long.
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 429
                  code:
                    enum:
                      - rate_limited
          examples:
            limited:
              value:
                type: https://docs.noresponse.org/problems/rate-limited
                title: Rate limited
                status: 429
                detail: >-
                  The tenant's assessment rate is exhausted; retry after the
                  Retry-After interval.
                code: rate_limited
                request_id: 0123456789abcdef0123456789abcdef
    ServiceUnavailable:
      description: >-
        The assessment could not be admitted at all (overload, shutdown or a
        dependency down). An assessment that was admitted answers partial or
        pending instead of 503.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        Retry-After:
          description: >-
            Whole seconds to wait before polling or retrying. Required on 202,
            429 and 503.
          schema:
            type: string
            pattern: >-
              ^(?:[1-9]|[1-9][0-9]|[1-9][0-9]{2}|[12][0-9]{3}|3[0-5][0-9]{2}|3600)$
            description: Whole decimal seconds; wait at least this long.
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 503
                  code:
                    enum:
                      - service_unavailable
  securitySchemes:
    tenantBearer:
      type: http
      scheme: bearer
      description: >-
        Your API key, sent as `Authorization: Bearer <key>`. A key is
        `nrk_live_` or `nrk_test_`, a 26-character key id, `_` and a
        43-character secret. A key sees only its own environment's data.


        Each key has one or more scopes: `assess` creates and reads assessments,
        records outcomes and reports key usage; `history` reads assessments, an
        account's current risk and an account's history, and erases an account;
        `detailed` adds the detailed view; `webhooks` registers, tests, rotates
        and deletes webhook endpoints; `integrations` registers and manages
        hosted provider integrations. A missing, unknown, revoked or expired key
        answers 401. A valid key without the scope an operation needs answers
        403.

````

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