curl --request POST \
--url 'https://api.noresponse.org/v1/webhooks' \
--header "Authorization: Bearer $NORESPONSE_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"url": "https://hooks.example.com/noresponse"
}'
import { NoResponse } from "@noresponse/node";
const noResponse = new NoResponse({
apiKey: process.env["NORESPONSE_API_KEY"] ?? "",
});
async function run() {
const result = await noResponse.webhookEndpoints.create({
url: "https://hooks.example.com/noresponse",
});
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.webhook_endpoints.create(url="https://hooks.example.com/noresponse")
# 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.WebhookEndpoints.Create(ctx, components.WebhookEndpointRequest{
URL: "https://hooks.example.com/noresponse",
})
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.CreateWebhookEndpointResponse;
import org.noresponse.models.shared.WebhookEndpointRequest;
public class Application {
public static void main(String[] args) throws Exception {
NoResponse sdk = NoResponse.builder()
.apiKey(System.getenv().getOrDefault("NORESPONSE_API_KEY", ""))
.build();
WebhookEndpointRequest req = WebhookEndpointRequest.builder()
.url("https://hooks.example.com/noresponse")
.build();
CreateWebhookEndpointResponse res = sdk.webhookEndpoints().create()
.request(req)
.call();
if (res.webhookEndpointCreated().isPresent()) {
System.out.println(res.webhookEndpointCreated().get());
}
}
}
using NoResponse;
using NoResponse.Models.Shared;
using System;
var sdk = new NoResponseClient(apiKey: Environment.GetEnvironmentVariable("NORESPONSE_API_KEY"));
WebhookEndpointRequest req = new WebhookEndpointRequest() {
Url = "https://hooks.example.com/noresponse",
};
var res = await sdk.WebhookEndpoints.CreateAsync(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\WebhookEndpointRequest(
url: 'https://hooks.example.com/noresponse',
);
$response = $sdk->webhookEndpoints->create(
request: $request
);
if ($response->webhookEndpointCreated !== null) {
// handle response
}
require 'noresponse'
Models = ::NoResponse::Models
s = ::NoResponse::NoResponseClient.new(
api_key: ENV.fetch('NORESPONSE_API_KEY', '')
)
req = Models::Shared::WebhookEndpointRequest.new(
url: 'https://hooks.example.com/noresponse'
)
res = s.webhook_endpoints.create(request: req)
unless res.nil?
# handle response
end
{
"endpoint": {
"endpoint_id": "whe_01JA2ZD0E1F2G3H4J5K6M7N8P9",
"url": "https://hooks.example.com/noresponse",
"state": "unverified",
"response_mode": "standard",
"created_at": "2026-10-22T09:00:00.000Z",
"signing_keys": [
{
"key_id": "whk_01JA2ZD1M2N3P4Q5R6S7T8V9W0",
"created_at": "2026-10-22T09:00:00.000Z"
}
]
},
"signing_secret": {
"key_id": "whk_01JA2ZD1M2N3P4Q5R6S7T8V9W0",
"secret": "Zm9yLWRvY3Mtb25seS1ub3QtYS1yZWFsLXNlY3JldDA",
"created_at": "2026-10-22T09:00:00.000Z"
}
}{
"type": "https://docs.noresponse.org/problems/webhook-url-refused",
"title": "Webhook URL refused",
"status": 400,
"detail": "The URL is not a permitted webhook destination.",
"code": "webhook_url_refused",
"request_id": "0123456789abcdef0123456789abcdef"
}{
"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/webhook-endpoint-limit-reached",
"title": "Webhook endpoint limit reached",
"status": 409,
"detail": "The tenant already has 5 webhook endpoints; delete one first.",
"code": "webhook_endpoint_limit_reached",
"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>"
}
]
}Register a webhook endpoint
Registers an endpoint and returns it with its first signing secret. The secret is shown only in this answer: store it where your receiver reads it.
The URL must be https on port 443 or 8443, name a host by DNS name, and resolve only to public addresses. It is checked now and on every delivery. You can hold 5 endpoints.
A new endpoint is unverified and receives no assessment event until it echoes an endpoint.verification challenge. Send one with POST /v1/webhooks/{endpoint_id}/test.
Requires the webhooks scope; response_mode: "detailed" also needs the detailed scope. Registration is not idempotent: a retry after a lost answer registers a second endpoint, which you can list and delete.
curl --request POST \
--url 'https://api.noresponse.org/v1/webhooks' \
--header "Authorization: Bearer $NORESPONSE_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"url": "https://hooks.example.com/noresponse"
}'
import { NoResponse } from "@noresponse/node";
const noResponse = new NoResponse({
apiKey: process.env["NORESPONSE_API_KEY"] ?? "",
});
async function run() {
const result = await noResponse.webhookEndpoints.create({
url: "https://hooks.example.com/noresponse",
});
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.webhook_endpoints.create(url="https://hooks.example.com/noresponse")
# 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.WebhookEndpoints.Create(ctx, components.WebhookEndpointRequest{
URL: "https://hooks.example.com/noresponse",
})
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.CreateWebhookEndpointResponse;
import org.noresponse.models.shared.WebhookEndpointRequest;
public class Application {
public static void main(String[] args) throws Exception {
NoResponse sdk = NoResponse.builder()
.apiKey(System.getenv().getOrDefault("NORESPONSE_API_KEY", ""))
.build();
WebhookEndpointRequest req = WebhookEndpointRequest.builder()
.url("https://hooks.example.com/noresponse")
.build();
CreateWebhookEndpointResponse res = sdk.webhookEndpoints().create()
.request(req)
.call();
if (res.webhookEndpointCreated().isPresent()) {
System.out.println(res.webhookEndpointCreated().get());
}
}
}
using NoResponse;
using NoResponse.Models.Shared;
using System;
var sdk = new NoResponseClient(apiKey: Environment.GetEnvironmentVariable("NORESPONSE_API_KEY"));
WebhookEndpointRequest req = new WebhookEndpointRequest() {
Url = "https://hooks.example.com/noresponse",
};
var res = await sdk.WebhookEndpoints.CreateAsync(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\WebhookEndpointRequest(
url: 'https://hooks.example.com/noresponse',
);
$response = $sdk->webhookEndpoints->create(
request: $request
);
if ($response->webhookEndpointCreated !== null) {
// handle response
}
require 'noresponse'
Models = ::NoResponse::Models
s = ::NoResponse::NoResponseClient.new(
api_key: ENV.fetch('NORESPONSE_API_KEY', '')
)
req = Models::Shared::WebhookEndpointRequest.new(
url: 'https://hooks.example.com/noresponse'
)
res = s.webhook_endpoints.create(request: req)
unless res.nil?
# handle response
end
{
"endpoint": {
"endpoint_id": "whe_01JA2ZD0E1F2G3H4J5K6M7N8P9",
"url": "https://hooks.example.com/noresponse",
"state": "unverified",
"response_mode": "standard",
"created_at": "2026-10-22T09:00:00.000Z",
"signing_keys": [
{
"key_id": "whk_01JA2ZD1M2N3P4Q5R6S7T8V9W0",
"created_at": "2026-10-22T09:00:00.000Z"
}
]
},
"signing_secret": {
"key_id": "whk_01JA2ZD1M2N3P4Q5R6S7T8V9W0",
"secret": "Zm9yLWRvY3Mtb25seS1ub3QtYS1yZWFsLXNlY3JldDA",
"created_at": "2026-10-22T09:00:00.000Z"
}
}{
"type": "https://docs.noresponse.org/problems/webhook-url-refused",
"title": "Webhook URL refused",
"status": 400,
"detail": "The URL is not a permitted webhook destination.",
"code": "webhook_url_refused",
"request_id": "0123456789abcdef0123456789abcdef"
}{
"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/webhook-endpoint-limit-reached",
"title": "Webhook endpoint limit reached",
"status": 409,
"detail": "The tenant already has 5 webhook endpoints; delete one first.",
"code": "webhook_endpoint_limit_reached",
"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/webhooks body. The workspace is the API key that signs the request, so it is not a body member. The URL must be https on port 443 or 8443 with a DNS name, not an IP literal, and every address the name resolves to must be public.
Where deliveries are sent: an https URL on port 443 or 8443, with a DNS host and no userinfo or fragment, at most 2048 characters.
12 - 2048^https://The view assessment.updated events carry: standard (the default) or detailed, which needs an API key with the detailed scope as well.
standard, detailed Response
The endpoint, unverified, and its first signing secret, shown once.
The 201 answer of POST /v1/webhooks: the new endpoint, unverified, and its first signing secret, shown once. Store the secret where the receiver reads it, then send endpoint.verification with POST /v1/webhooks/{endpoint_id}/test.
One of the workspace's registered webhook endpoints. Signing secrets are not part of it: a secret is shown once, when it is issued (POST /v1/webhooks and POST /v1/webhooks/{endpoint_id}/rotate).
Show child attributes
Show child attributes
A newly issued signing key with its secret, shown only in this answer. A receiver verifies NR-Webhook-Signature with the secret's UTF-8 bytes as the HMAC-SHA256 key.
Show child attributes
Show child attributes