curl --request POST \
--url 'https://api.noresponse.org/v1/keys/rotate' \
--header "Authorization: Bearer $NORESPONSE_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"overlap_seconds": 3600
}'
import { NoResponse } from "@noresponse/node";
const noResponse = new NoResponse({
apiKey: process.env["NORESPONSE_API_KEY"] ?? "",
});
async function run() {
const result = await noResponse.keys.rotate({
overlapSeconds: 3600,
});
console.log(result);
}
run();
from noresponse import NoResponse
import os
with NoResponse(
api_key=os.getenv("NORESPONSE_API_KEY", ""),
) as no_response:
res = no_response.keys.rotate(overlap_seconds=3600)
# Handle response
print(res)
package main
import(
"context"
"os"
noresponse "github.com/noresponsehq/go/v5"
"github.com/noresponsehq/go/v5/models/components"
"log"
)
func main() {
ctx := context.Background()
s := noresponse.New(
noresponse.WithSecurity(os.Getenv("NORESPONSE_API_KEY")),
)
res, err := s.Keys.Rotate(ctx, components.KeyRotationRequest{
OverlapSeconds: noresponse.Pointer[int64](3600),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
package hello.world;
import java.lang.Exception;
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.RotateApiKeyResponse;
import org.noresponse.models.shared.KeyRotationRequest;
public class Application {
public static void main(String[] args) throws Exception {
NoResponse sdk = NoResponse.builder()
.apiKey(System.getenv().getOrDefault("NORESPONSE_API_KEY", ""))
.build();
KeyRotationRequest req = KeyRotationRequest.builder()
.overlapSeconds(3600L)
.build();
RotateApiKeyResponse res = sdk.keys().rotate()
.request(req)
.call();
if (res.keyRotation().isPresent()) {
System.out.println(res.keyRotation().get());
}
}
}
using NoResponse;
using NoResponse.Models.Shared;
using System;
var sdk = new NoResponseClient(apiKey: Environment.GetEnvironmentVariable("NORESPONSE_API_KEY"));
KeyRotationRequest req = new KeyRotationRequest() {
OverlapSeconds = 3600,
};
var res = await sdk.Keys.RotateAsync(req);
// handle response
<?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();
$request = new Shared\KeyRotationRequest(
overlapSeconds: 3600,
);
$response = $sdk->keys->rotate(
request: $request
);
if ($response->keyRotation !== null) {
// handle response
}
require 'noresponse'
Models = ::NoResponse::Models
s = ::NoResponse::NoResponseClient.new(
api_key: ENV.fetch('NORESPONSE_API_KEY', '')
)
req = Models::Shared::KeyRotationRequest.new(
overlap_seconds: 3600
)
res = s.keys.rotate(request: req)
unless res.nil?
# handle response
end
{
"key": "nrk_live_01JA2ZC0Q1R2S3T4V5W6X7Y8Z9_0123456789abcdefghijklmnopqrstuvwxyzABCDEFA",
"key_id": "01JA2ZC0Q1R2S3T4V5W6X7Y8Z9",
"scopes": [
"assess",
"history"
],
"created_at": "2026-10-21T09:00:00.000Z",
"previous_key_id": "01JA2Z8K6Q4V7W9XBCDEFGHJKM",
"previous_key_expires_at": "2026-10-22T09:00:00.000Z"
}{
"type": "<string>",
"title": "<string>",
"status": 400,
"detail": "<string>",
"code": "invalid_request",
"request_id": "<string>",
"instance": "<string>",
"invalid_params": [
{
"field": "<string>",
"reason": "<string>",
"example": "<string>"
}
]
}{
"type": "<string>",
"title": "<string>",
"status": 401,
"detail": "<string>",
"code": "unauthorized",
"request_id": "<string>",
"instance": "<string>",
"invalid_params": [
{
"field": "<string>",
"reason": "<string>",
"example": "<string>"
}
]
}{
"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"
}{
"type": "https://docs.noresponse.org/problems/key-rotation-in-progress",
"title": "Key rotation in progress",
"status": 409,
"detail": "This API key was already rotated and is in its overlap window; rotate with the new key.",
"code": "key_rotation_in_progress",
"request_id": "0123456789abcdef0123456789abcdef"
}{
"type": "<string>",
"title": "<string>",
"status": 413,
"detail": "<string>",
"code": "payload_too_large",
"request_id": "<string>",
"instance": "<string>",
"invalid_params": [
{
"field": "<string>",
"reason": "<string>",
"example": "<string>"
}
]
}{
"type": "<string>",
"title": "<string>",
"status": 415,
"detail": "<string>",
"code": "unsupported_media_type",
"request_id": "<string>",
"instance": "<string>",
"invalid_params": [
{
"field": "<string>",
"reason": "<string>",
"example": "<string>"
}
]
}{
"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"
}{
"type": "<string>",
"title": "<string>",
"status": 503,
"detail": "<string>",
"code": "service_unavailable",
"request_id": "<string>",
"instance": "<string>",
"invalid_params": [
{
"field": "<string>",
"reason": "<string>",
"example": "<string>"
}
]
}Rotate your API key
Creates a new key with the same scopes as the key that signs this request, and returns it once. Store it straight away: it is never shown again.
The old key keeps working for overlap_seconds (24 hours by default, 60 seconds to 7 days), so you can roll the new key out without a failed request. After the overlap the old key answers 401.
A key can be rotated once. Rotating a key that is already in its overlap window answers 409 key_rotation_in_progress; rotate with the new key instead. Rotation is not idempotent: a retry after a lost answer creates another key. Any scope may rotate its own key.
curl --request POST \
--url 'https://api.noresponse.org/v1/keys/rotate' \
--header "Authorization: Bearer $NORESPONSE_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"overlap_seconds": 3600
}'
import { NoResponse } from "@noresponse/node";
const noResponse = new NoResponse({
apiKey: process.env["NORESPONSE_API_KEY"] ?? "",
});
async function run() {
const result = await noResponse.keys.rotate({
overlapSeconds: 3600,
});
console.log(result);
}
run();
from noresponse import NoResponse
import os
with NoResponse(
api_key=os.getenv("NORESPONSE_API_KEY", ""),
) as no_response:
res = no_response.keys.rotate(overlap_seconds=3600)
# Handle response
print(res)
package main
import(
"context"
"os"
noresponse "github.com/noresponsehq/go/v5"
"github.com/noresponsehq/go/v5/models/components"
"log"
)
func main() {
ctx := context.Background()
s := noresponse.New(
noresponse.WithSecurity(os.Getenv("NORESPONSE_API_KEY")),
)
res, err := s.Keys.Rotate(ctx, components.KeyRotationRequest{
OverlapSeconds: noresponse.Pointer[int64](3600),
})
if err != nil {
log.Fatal(err)
}
if res != nil {
// handle response
}
}
package hello.world;
import java.lang.Exception;
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.RotateApiKeyResponse;
import org.noresponse.models.shared.KeyRotationRequest;
public class Application {
public static void main(String[] args) throws Exception {
NoResponse sdk = NoResponse.builder()
.apiKey(System.getenv().getOrDefault("NORESPONSE_API_KEY", ""))
.build();
KeyRotationRequest req = KeyRotationRequest.builder()
.overlapSeconds(3600L)
.build();
RotateApiKeyResponse res = sdk.keys().rotate()
.request(req)
.call();
if (res.keyRotation().isPresent()) {
System.out.println(res.keyRotation().get());
}
}
}
using NoResponse;
using NoResponse.Models.Shared;
using System;
var sdk = new NoResponseClient(apiKey: Environment.GetEnvironmentVariable("NORESPONSE_API_KEY"));
KeyRotationRequest req = new KeyRotationRequest() {
OverlapSeconds = 3600,
};
var res = await sdk.Keys.RotateAsync(req);
// handle response
<?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();
$request = new Shared\KeyRotationRequest(
overlapSeconds: 3600,
);
$response = $sdk->keys->rotate(
request: $request
);
if ($response->keyRotation !== null) {
// handle response
}
require 'noresponse'
Models = ::NoResponse::Models
s = ::NoResponse::NoResponseClient.new(
api_key: ENV.fetch('NORESPONSE_API_KEY', '')
)
req = Models::Shared::KeyRotationRequest.new(
overlap_seconds: 3600
)
res = s.keys.rotate(request: req)
unless res.nil?
# handle response
end
{
"key": "nrk_live_01JA2ZC0Q1R2S3T4V5W6X7Y8Z9_0123456789abcdefghijklmnopqrstuvwxyzABCDEFA",
"key_id": "01JA2ZC0Q1R2S3T4V5W6X7Y8Z9",
"scopes": [
"assess",
"history"
],
"created_at": "2026-10-21T09:00:00.000Z",
"previous_key_id": "01JA2Z8K6Q4V7W9XBCDEFGHJKM",
"previous_key_expires_at": "2026-10-22T09:00:00.000Z"
}{
"type": "<string>",
"title": "<string>",
"status": 400,
"detail": "<string>",
"code": "invalid_request",
"request_id": "<string>",
"instance": "<string>",
"invalid_params": [
{
"field": "<string>",
"reason": "<string>",
"example": "<string>"
}
]
}{
"type": "<string>",
"title": "<string>",
"status": 401,
"detail": "<string>",
"code": "unauthorized",
"request_id": "<string>",
"instance": "<string>",
"invalid_params": [
{
"field": "<string>",
"reason": "<string>",
"example": "<string>"
}
]
}{
"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"
}{
"type": "https://docs.noresponse.org/problems/key-rotation-in-progress",
"title": "Key rotation in progress",
"status": 409,
"detail": "This API key was already rotated and is in its overlap window; rotate with the new key.",
"code": "key_rotation_in_progress",
"request_id": "0123456789abcdef0123456789abcdef"
}{
"type": "<string>",
"title": "<string>",
"status": 413,
"detail": "<string>",
"code": "payload_too_large",
"request_id": "<string>",
"instance": "<string>",
"invalid_params": [
{
"field": "<string>",
"reason": "<string>",
"example": "<string>"
}
]
}{
"type": "<string>",
"title": "<string>",
"status": 415,
"detail": "<string>",
"code": "unsupported_media_type",
"request_id": "<string>",
"instance": "<string>",
"invalid_params": [
{
"field": "<string>",
"reason": "<string>",
"example": "<string>"
}
]
}{
"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"
}{
"type": "<string>",
"title": "<string>",
"status": 503,
"detail": "<string>",
"code": "service_unavailable",
"request_id": "<string>",
"instance": "<string>",
"invalid_params": [
{
"field": "<string>",
"reason": "<string>",
"example": "<string>"
}
]
}Authorizations
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.
Body
POST /v1/keys/rotate body. The workspace and the key being rotated are the API key that signs the request, so neither is a body member. An empty object takes the default overlap.
How long the presented key keeps working beside the new one, from 60 seconds to 7 days. Default 86400 (24 hours). At the end of the overlap the presented key stops authenticating.
60 <= x <= 604800Response
The new key, shown once. Both keys authenticate until previous_key_expires_at; after it only the new key does.
The 201 answer of POST /v1/keys/rotate: the new key, shown once, and when the key that was presented stops working. The new key has the scopes and the environment of the presented key.
The new credential, nrk_, in the environment of the presented key: nrk_live_ or nrk_test_. It is shown only in this answer and is never stored or returned again.
^nrk_(live|test)_[0-7][0-9A-HJKMNP-TV-Z]{25}_[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$The public id of the new key.
^[0-7][0-9A-HJKMNP-TV-Z]{25}$The scopes of the new key: those of the presented key.
1assess, history, webhooks, detailed, integrations The id of the key that signed this request.
^[0-7][0-9A-HJKMNP-TV-Z]{25}$When the previous key stops authenticating. Until then both keys work.