Page contents

API version 2026-09-21

API reference

External integration (Management API)

Connect Usakey to the order management, billing, CRM, and support systems you already use. This page calls those systems “your systems.”

How to read this page

What you will learn

  • How your systems issue and change licenses
  • How your systems receive changes from Usakey
  • API-key scope design and webhook signature verification
  • How to operate through an AI assistant using MCP

Prerequisites

  • Products and license policies are registered in Console
  • You can implement API calls on your server
  • You can provide a publicly reachable HTTPS URL for webhooks

Fastest path

  1. Create an API key with only the required scopes
  2. Try issuing one license
  3. Receive changes through webhooks

The Product app integration (Runtime API) page explains how to integrate authentication into your product app.

Integration overview

There are two directions: your systems operate Usakey, and Usakey reports changes back to your systems. Use API keys for the first direction and webhooks for the second.

Sales, billing, CRM

A contract is created or changed

Start with orders, payments, renewals, cancellations, and other information owned by your systems.

API-key operation

Sync customers and licenses

Use an API key with only the required scopes to issue, update, suspend, resume, or revoke.

Usakey

Apply contract terms to the product

The product app receives the current license state on its next check.

Webhook

Report changes to your systems

Usakey sends signed JSON for issuance, expiry, suspension, activation, and other changes.

Figure 1: API keys let your systems operate Usakey; webhooks let your systems receive changes that occur in Usakey.

Your systems → Usakey

Using API keys

An API key is the credential your systems use to call Usakey. Create keys in the API keys screen and grant each integration only the smallest set of required operations.

Where to create it
In Console, open “Development & integrations” → “API keys” → “Create API key”. Before creating or revoking a key, Console asks you to confirm your identity within the last 10 minutes (a verification code if you enrolled two-factor authentication, otherwise your password).
Where to store it
Store it in your server’s secret manager. Never put it in a browser or an app distributed to customers.
How to send it
Set Authorization: Bearer <API key> as an HTTP header.
Scopes
For example, read licenses with licenses:read and issue or change them with licenses:write. Use separate keys for separate purposes. The table below lists every scope.
Environment and expiry
Each key belongs to either test or live. To create a live key, the person creating it must have two-factor authentication set up. You can also set an expiry: choose one per integration and plan how to rotate the key.
ScopeWhat it allows
products:readView productsRetrieve registered product information.
products:writeManage productsCreate, update, and archive products.
policies:readView policiesRetrieve device limits, expiration rules, and other policy settings.
policies:writeManage policiesCreate and update license usage policies.
customers:readView customersRetrieve customer records.
customers:writeManage customersCreate and update customers, customer organizations, resellers and license users (not end-user sign-in settings).
licenses:readView licensesRetrieve license status and expiration information.
licenses:writeManage licensesIssue, update, suspend, resume, and revoke licenses.
activations:deleteDeactivate devicesRemove lost or retired devices from a license.
webhooks:readView webhook settingsRetrieve registered webhook destinations.
webhooks:writeManage webhook settingsCreate, update, and disable webhook destinations.
events:readView audit eventsRetrieve administrative actions and device authentication events.
reports:readView usage reportsRetrieve license and device usage totals.
sso:readView sign-in settingsRetrieve the OIDC connections (identity providers) used to sign in to the customer portal.
sso:writeManage sign-in settingsChange an OIDC connection's issuer, endpoints, key location and other settings. This decides who can sign in to the customer portal, so grant it only to integrations that need it.

Managing the customer portal's OIDC connections (sign-in settings) requires sso:read / sso:write. They used to be covered by customers:read / customers:write, but the permission that can replace an identity provider is now separate from customer records. Existing API keys that manage OIDC connections get SCOPE_REQUIRED (HTTP 403) until they have the new permissions. API-key permissions cannot be changed after creation, so create a new key with sso:read / sso:write, switch your integration to it, and revoke the old key.

Choose the right integration

Billing

Issue a license after payment

Register the customer and issue a license for the purchased product, policy, and term.

Contract management

Sync renewals, suspensions, and resumes

Reflect contract changes in Usakey so the product app receives the new state on its next check.

CRM and support

Check contract and device status

Review license state, expiry, and activated devices when responding to a customer.

Operations reporting

Aggregate usage

Retrieve product and contract usage for renewal guidance and customer support.

Your systems → Usakey

Managing licenses

Issue, update, suspend, resume, and revoke licenses through APIs under /mgmt/v1/. These endpoints are called the Management API. The example below issues a license after billing confirms payment.

Send an Idempotency-Key header with creation requests (POST). It is required when you issue a license, convert a trial, or create a customer, product, policy, reseller, customer organization, organization group, group membership, license user, OIDC connection, or license assignment; without it, the request is rejected with IDEMPOTENCY_KEY_REQUIRED. It is optional when you create a webhook endpoint, and sending one there prevents duplicates in the same way. It is not required for updates (PATCH) or deletes (DELETE).

POST /mgmt/v1/licenses
Authorization: Bearer usk_live_…
Idempotency-Key: 7e2c4d5a-6f44-4b8f-90f4-4ee9b34d66e2
Content-Type: application/json

{
  "product_id": "prod_01K123EXAMPLE",
  "policy_id": "pol_01K123EXAMPLE",
  "customer_id": "cus_01K123EXAMPLE",
  "expires_at": "2027-08-24T00:00:00Z",
  "entitlements": { "export_pdf": true }
}
product_id
Selects the product covered by the license. It is shown in the product details in Console.
policy_id
The policy that defines device limits and offline behavior. Create it in the Policies screen.
customer_id
The customer receiving the license. If omitted, the license is not associated with a customer.
expires_at
The contract expiry in ISO 8601. If no time zone is given, it is interpreted as Japan time. Omit it for perpetual licenses. It is required on Evaluation and must be within one hour of issuance. The legacy name expiry remains accepted for now but is deprecated.
entitlements
Names of capabilities the product app should enable. This example means “PDF export is available.”

This operation requires the licenses:write scope.

Generate a unique UUID as the Idempotency-Key for each new operation. Reuse it only when retrying the same operation after an unknown result. Sending a different body with the same key returns IDEMPOTENCY_CONFLICT. Signing-secret rotations (webhook and OIDC) and offline file issuance do not support idempotency keys. If the result of scheduling a webhook signing-secret rotation is unknown, a retry fails with WEBHOOK_SECRET_ROTATION_INVALID because the rotation is already scheduled; cancel it (DELETE) and schedule it again. Retry offline file issuance with the same request file and the same settings. This key is separate from the key used for Runtime activation or Stripe license claims; do not mix them.
Product signing keys are prepared automatically when needed. If HTTP 409 returns PRODUCT_SIGNING_KEY_PENDING with retryable: true, wait a few minutes and retry with the same body and the same Idempotency-Key. No product or SDK configuration change is required.

Do not poll every second. Management API calls have three limits.

  • Per API key: 30 requests/minute and 300/hour on every plan
  • Across all keys in the workspace: Evaluation 30/minute and 300/hour; Personal 30/minute and 300/hour; Team 120/minute and 5,000/hour; Enterprise 600/minute and 20,000/hour. On Evaluation and Personal, adding keys does not raise the workspace-wide limit.
  • Per client IP address: 120/minute

Use webhooks to detect changes. After HTTP 429, wait Retry-After seconds before retrying; 429 responses also include RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset.

The setting that lets the product app start a trial with email verification (the trial policy it uses and the 24-hour limit) can be read and changed with GET / PATCH /mgmt/v1/products/{product_id}/trial-signup (products:read / products:write) as well as on the product's edit page in the Console. The checks are the same as in the Console, and choosing a policy needs a plan that includes trials.

Sealed files for a product (files shipped with the product that only devices with a valid license can open) can be read and turned on with GET / POST /mgmt/v1/products/{product_id}/sealed-files (products:read / products:write) as well as on the product's page in the Console. POST takes no body and gives the same result however many times it is sent (it does not use Idempotency-Key). Once on, sealed files cannot be turned off. The Team plan or higher is required; otherwise the response is PLAN_ENTITLEMENT_REQUIRED. The response carries current_key right away; if the sealing key can't be reached for a moment, the response is 503 SEALED_FILES_KEY_UNAVAILABLE, so send it again later. current_key.sealing_key is the one-line public key for the sealing tool and is not a secret. The files themselves are never sent to Usakey. Sealing does not stop an administrator of a licensed device from extracting the data after the app opens it, and it does not prove who made a file, so have the app check a hash of files that must not be swapped.

The API reference contains customers, policies, activations, audit events, and all field definitions.

Your systems → Usakey

Operate from an AI assistant (MCP)

AI assistants such as Claude Code and Codex can operate this page’s APIs through conversation. MCP (Model Context Protocol) is a common way for an AI assistant to call external services. Usakey provides https://usakey.jp/mcp; add it to your assistant and sign in in a browser, then ask for tasks such as “suspend this license.” The same connection can read skills for integrating a product app.

Example requests

  • List licenses that have expired
  • Register Sample Corporation and issue a license with the standard policy
  • Suspend this license because payment is overdue
  • Show this license’s devices and deactivate the old PC
  • Report usage for the last 30 days and today’s change history
  • Use Usakey’s MCP skill to add license authentication to this app

What it can do

  • The AI can run only the operations allowed in the browser confirmation screen. Disallowed tools are not shown to it.
  • It can work with products, policies, customers, licenses, activations, audit events, usage, webhooks, user-level licensing, and the customer portal's sign-in settings (OIDC connections). It can also check the integration settings, plan limits and this month's usage, device check-in status, and your Stripe integration. It can view and change whether a product accepts trials started from the app with get_product_trial_signup / update_product_trial_signup. For sealed files, get_product_sealed_files shows the status and the sealing key, and enable_product_sealed_files turns them on (there is no tool to turn them off).
  • The AI developer skill delivered through the same MCP handles product-app integration.
  • Deleting products, creating OIDC connections, and changing their client secrets stay in Console so that IdP secrets are not left in the conversation.

Setup flow

  1. Add the MCP server https://usakey.jp/mcp to your AI assistant by giving it instructions or running the command below.
  2. Sign in to Usakey in a browser and choose the workspace and permitted scopes in the confirmation screen. For management operations, start with test and the smallest necessary scope. To allow “Allow day-to-day operations” or more in production or both, the person granting it must have two-factor authentication set up.
  3. Ask the AI assistant in English or Japanese. The assistant maintains and refreshes the login state.
# Claude Code (after adding, choose usakey in /mcp and Authenticate)
claude mcp add --transport http --scope user usakey https://usakey.jp/mcp

# Codex (the browser login starts after adding it)
codex mcp add usakey --url https://usakey.jp/mcp

The Product app integration (Runtime API) quickstart explains the prompt and confirmation choices. Revoke the connection at any time from AI assistant connections in Console.

Permissions and API scopes

Use skills only
Read skills only; there are no management tools. Any role can choose this.
Allow read-only access
products:read policies:read customers:read licenses:read events:read reports:read
Allow day-to-day operations
Also allow customers:write licenses:write activations:delete to register customers, issue or change licenses, and deactivate devices.
Allow product, policy, and webhook configuration
Also allow products:write policies:write webhooks:read and webhooks:write to create and change products and policies and to add, change, or rotate the secret of webhook destinations.
User-level licensing tools
Select “Also allow tools for user-level licenses (resellers, organizations, groups, and license users)” on the consent screen to add tools for resellers, customer organizations, groups, license users, and assignments. They work within the customers:read / customers:write access above. They are checked by default on plans that include them (Personal and above). On Personal, customer organizations and resellers are not available (Team and above).
Sign-in settings tools
Select the sign-in settings tools (the customer portal's OIDC connections and identity providers) on the consent screen to get sso:read and view OIDC connections. With “Allow product, policy, and webhook configuration” it also gets sso:write and can change or disable them. This decides who can sign in to the customer portal, so choose it only when needed (it starts unchecked and is available on Team and above).

Only workspace owners and admins can grant management operations. Usakey creates a connection-specific API key with the chosen scopes and environment, but never displays the key value. The connection stops working if the person who granted it leaves the workspace or loses permission to grant management access. The initial choice on the consent screen is “Allow read-only access” when Usakey could verify the app's publisher and your role can grant management access; otherwise it is “Use skills only”.

Environments and “both”

When granting management access, choose the environment on the consent screen.

  • Test (test-environment products only)
  • Production (changes licenses for distributed products)
  • Both (test and production products)

A connection with both environments holds one API key per environment with the same permissions, so “API keys” in Console lists two keys for the connection. Two-factor authentication is required under the same conditions as production. Each time the assistant calls a tool, Usakey picks the key in this order.

  1. The tool argument environment (test or live). It appears only on connections with both and is not sent to the API.
  2. The environment of the target named by an ID (license, then activation, policy, and product). When environment and the target's environment differ, Usakey stops without calling the API and returns ENVIRONMENT_REQUIRED with a hint.
  3. Lists without an ID (list_products, list_policies, list_licenses, list_runtime_events) called without environment run on both environments, merge the results, and tag each item with environment. That uses two monthly calls. The next page comes from a cursor that keeps the position in each environment.
  4. Creating a product (create_product) requires environment. Product names and codes are unique across test and live, so give the test and live copies different names and codes.
  5. The usage report (get_usage_report) needs environment or product_id. Test and live numbers are not added together.
  6. What is not kept per environment (customers, user-level licensing, sign-in settings, webhooks, audit logs, the plan, and your own Stripe integration) uses the production key.

The operation history in Console shows which environment's key the actor used (for example, “MCP: Codex / … (Production key)”). You can change the environment, permissions, and tool sets later with “Edit settings” in AI assistant connections (Console asks you to confirm your identity within the last 10 minutes before saving). Widening test to both keeps the current key; keys for a removed environment or for changed permissions are recreated and the old ones are revoked.

In the usage report (get_usage_report), active_machines is the number of devices activated in the period and not deactivated. It includes devices of suspended and expired licenses, so it is not the number of devices that can use the product right now.

Options your plan does not include are dimmed on the consent screen with one of these reasons.

  • This workspace's plan can't create production products, so Production and Both aren't available (Personal plan or higher).
  • This workspace's plan doesn't include user-level licenses (Personal plan or higher).
  • This workspace's plan doesn't include customer organizations or resellers (Team plan or higher).
  • This workspace's plan doesn't include OIDC sign-in for the customer portal (Team plan or higher).

Use it safely

  • Do not auto-approve mutation tools. In particular, revoke_license cannot be undone. Each tool is marked as read, create, or change for the assistant.
  • Operations that cannot be undone run only when the target is confirmed: the last four characters of the key (confirm_key_last4) to revoke a license, and the name (confirm_name) to retire a policy (retire_policy) or archive a customer (archive_customer). Before approving, check that the assistant got the value from you.
  • Customer names, notes, and metadata may contain instructions aimed at the AI. A person must approve changes, and permissions should be minimal.
  • When the AI runs the bundled SDK-download script, review the command in the assistant’s confirmation screen before approving it.
A license key issued through Usakey's MCP is included in the response and remains in the AI conversation and its record. To keep it out, ask the assistant to set reveal_key: false when issuing; the key is left out of the response, and the full key can be shown on the license page in Console. Webhook signing secrets are available only in the creation response, so create webhook destinations in Console if you do not want the secret in the conversation.
Each management-tool call makes one Management API call. It uses the plan’s monthly allowance and the workspace-wide per-minute and per-hour limits. get_plan_limits and calls refused without changing anything (invalid input, a state mismatch, or a signing key still being prepared) do not count toward the monthly allowance, and reading a skill uses no calls. See the pricing page for each plan’s allowance. Lists return 20 items by default (the assistant may request up to 200) and are not walked automatically to the end.

Supported protocol details

  • MCP 2026-07-28 (Streamable HTTP, stateless requests with _meta, MCP-Protocol-Version, Mcp-Method, and Mcp-Name headers, plus server/discover) and the 2025-03-26 through 2025-11-25 initialize flow
  • The io.modelcontextprotocol/skills extension (skills/list, skills/get, resources/directory/read) and skill:// resources
  • OAuth 2.1 authorization code with required PKCE S256, refresh-token rotation, Protected Resource Metadata (RFC 9728), authorization-server metadata (RFC 8414), Client ID Metadata Document, dynamic client registration (RFC 7591), resource (RFC 8707), and iss (RFC 9207)

Local server with an API key (optional)

The official SDK also includes a local MCP server at usakey-sdk/mcp/. It uses an API key instead of browser login, making it suitable for CI and other environments without a browser. Only this local server can issue offline activation files and save license keys to a file without exposing them in the conversation. It does not provide skills; install skills through the MCP above or the ZIP.

Setup flow

  1. Install Node.js 20.3 or later. No additional package is required.
  2. Download the official SDK, verify its checksum, and extract it. The server is under usakey-sdk/mcp/.
  3. Create a dedicated API key in Console under “Development & integrations” → “API keys”. Start with test and the smallest necessary scopes. Store the key in a file readable only by you.
  4. Check the connection, then register it with your AI assistant.

The following Linux/macOS example extracts into the home directory. At the read line, paste the API key and press Enter; it is not kept on screen or in shell history. On Windows, verify the checksum with PowerShell Get-FileHash -Algorithm SHA256.

cd ~
curl -fSLO https://usakey.jp/downloads/usakey-sdk.zip
curl -fSLO https://usakey.jp/downloads/usakey-sdk.zip.sha256
sha256sum -c usakey-sdk.zip.sha256    # on macOS, use shasum -a 256 -c
unzip -q usakey-sdk.zip

mkdir -p ~/.usakey && chmod 700 ~/.usakey
read -rs USAKEY_KEY
(umask 077 && printf '%s\n' "$USAKEY_KEY" > ~/.usakey/api-key)
unset USAKEY_KEY

USAKEY_API_URL=https://usakey.jp \
USAKEY_MANAGEMENT_API_KEY_FILE=$HOME/.usakey/api-key \
node ~/usakey-sdk/mcp/bin/usakey-mcp.mjs --check

The local server prints its messages in Japanese for now. The check exits with status 0 when the API key is valid, and with status 1 when the connection fails. This check makes one Management API call.

Register it with an AI assistant

claude mcp add usakey --scope user \
  -e USAKEY_API_URL=https://usakey.jp \
  -e USAKEY_MANAGEMENT_API_KEY_FILE=$HOME/.usakey/api-key \
  -- node $HOME/usakey-sdk/mcp/bin/usakey-mcp.mjs

Run claude mcp get usakey; ✔ Connected means it is registered. If sharing through a repository .mcp.json, read the key path from an environment variable such as "${USAKEY_MANAGEMENT_API_KEY_FILE}" and never put the secret in the configuration.

Choose API-key scopes

Read status only
products:read policies:read customers:read licenses:read events:read reports:read. Set USAKEY_MCP_READ_ONLY=true to hide mutation tools.
Customers and licenses too
Also allow customers:write licenses:write activations:delete to register customers, issue or suspend licenses, and deactivate devices.
Products and policies too
Also allow products:write and policies:write.
Webhooks, user-level licensing, and sign-in settings
Add the tools you need, for example USAKEY_MCP_TOOLSETS=core,webhooks,directory,sso, and give the key webhooks:read / webhooks:write for webhooks, customers:read / customers:write for user-level licensing (resellers, customer organizations, groups, and license users), and sso:read / sso:write for sign-in settings (OIDC connections). customers:write cannot manage OIDC connections.

Use the local server safely

  • Do not auto-approve mutation tools. In particular, revoke_license cannot be undone.
  • Data from Usakey, including customer names, notes, and metadata, may contain instructions aimed at the AI. A person must approve changes and the API key must be minimal.
  • The local server reads files only when you provide an offline activation request (.usakeyreq); it rejects other file formats before sending them.
License keys and webhook signing secrets are available only in the creation response. To keep them out of the conversation, ask the assistant to save a key to a path such as ~/keys/sample.key. The key is written to a file readable only by its owner and removed from the assistant response.

The full tool list, configuration, and troubleshooting steps are in usakey-sdk/mcp/README.md.

Usakey → your systems

Webhooks

Usakey can automatically report license issuance, expiry, suspension, resumption, revocation, and new device activations to your systems. Your CRM, customer notifications, and renewal workflows can start from those events, so polling Usakey is usually unnecessary (see the monthly allowance below).

Usakey

Detect an event

Detect issuance, expiry, suspension, activation, and other changes.

Signed webhook

Send to the registered HTTPS URL

Send the event ID, timestamp, JSON body, and tamper-detection signature.

Your systems

Verify and deduplicate

Verify the signature before processing the body and use the event ID to prevent duplicate work.

Follow-up work

Record receipt and return 2xx

Update CRM, notices, and records asynchronously after returning 2xx. Deliveries without a 2xx within 30 seconds are retried.

Figure 2: A webhook does not ask Usakey for changes; it lets Usakey report changes to your systems.

Besides your own systems (signed JSON), you can send notifications to Slack, Microsoft Teams, Google Chat, or Chatwork. Chat notifications are not signed, so use a notification to your own system to start business processes.

Configure and receive webhooks

  1. In Console, open “Development & integrations” → “Webhooks” and register a public HTTPS URL and the events to send. Changing or deleting an endpoint later, or switching its signing secret, asks you to confirm your identity within the last 10 minutes.
  2. Store the signing secret, shown only once, in a secure area of your server.
  3. Verify the signature against the body and headers exactly as received, before parsing the JSON.
  4. Record X-Usakey-Event-Id to prevent duplicate work and return HTTP 2xx right away. Run follow-up work, such as CRM updates, asynchronously after returning 2xx.

Usakey treats a delivery that does not receive 2xx within 30 seconds as failed and retries it. If you finish heavy processing before responding, the same event can arrive again and again.

The same event can arrive more than once. Connection errors and HTTP 408, 425, 429, or 5xx responses are retried for up to 24 hours. Other 4xx responses are treated as non-retryable.

Each webhook delivery attempt uses your plan's monthly allowance. Every attempt counts once, including each retry. “Send test event” and “Resend” in Console also count once each. The monthly allowance is Evaluation 100, Personal 2,000, Team 20,000, and Enterprise 200,000 (it resets on the 1st of each month, Japan time). After the allowance is used up, later events that month are neither delivered nor retried; Usakey only records in the Console “Audit log” that the limit was reached. Retries during an outage on your side can use up the allowance quickly, so for important workflows also reconcile with a Management API list call about once a day. Additional webhook deliveries are not currently sold as an add-on, so a larger allowance requires a higher plan.

Verifying webhook signatures

Webhooks to your own systems include these headers (chat notifications have no signature headers).

X-Usakey-Event-Id
The unique event ID. It stays the same across retries, so use it for deduplication.
X-Usakey-Delivery-Id
The ID of one delivery attempt. It changes on each retry, so do not use it for deduplication.
X-Usakey-Timestamp
The Unix timestamp in seconds when the event was sent.
X-Usakey-Signature
The signature used to verify the sender and detect tampering.
User-Agent
Usakey-Webhook/1.0

HMAC-SHA256 creates a verification value from the shared secret known only to Usakey and the receiver. The signed value is timestamp.event_id.raw_body, and the result is v1=<lowercase hex>. raw_body means the received body exactly as received, including whitespace, line breaks, and key order. Verify it before parsing and rebuilding JSON. Use the signing-secret string exactly as displayed (its UTF-8 bytes) as the HMAC key; do not decode it as Base64URL.

Reject events whose X-Usakey-Timestamp differs from the current time by more than five minutes (a recommendation; Usakey does not set a receiver-side tolerance). During a signing-secret rotation, deliveries switch to the new secret at the switch time. Around that time, accept signatures made with either the old or the new secret.

Verification examples

Ruby

require "openssl"

# raw_body: the body exactly as received, before parsing the JSON
# secrets: signing-secret strings (both old and new during a rotation)
def valid_usakey_signature?(raw_body, headers, secrets, now: Time.now.to_i)
  timestamp = headers.fetch("X-Usakey-Timestamp")
  event_id = headers.fetch("X-Usakey-Event-Id")
  signature = headers.fetch("X-Usakey-Signature")
  return false if (now - Integer(timestamp, 10)).abs > 300 # reject events more than five minutes off

  signed = "#{timestamp}.#{event_id}.".b + raw_body.b
  secrets.any? do |secret|
    expected = "v1=" + OpenSSL::HMAC.hexdigest("SHA256", secret, signed)
    OpenSSL.secure_compare(expected, signature)
  end
end

Node.js

import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the body exactly as received (Buffer); verify before JSON.parse
// secrets: signing-secret strings (both old and new during a rotation)
export function isValidUsakeySignature(rawBody, headers, secrets, now = Math.floor(Date.now() / 1000)) {
  const timestamp = headers["x-usakey-timestamp"] ?? "";
  const eventId = headers["x-usakey-event-id"] ?? "";
  const signature = Buffer.from(headers["x-usakey-signature"] ?? "");
  if (!/^\d+$/.test(timestamp) || Math.abs(now - Number(timestamp)) > 300) return false; // reject events more than five minutes off

  const signed = Buffer.concat([Buffer.from(`${timestamp}.${eventId}.`), rawBody]);
  return secrets.some((secret) => {
    const expected = Buffer.from(`v1=${createHmac("sha256", secret).update(signed).digest("hex")}`);
    return expected.length === signature.length && timingSafeEqual(expected, signature);
  });
}

Webhook events and payloads

Use the human-readable event label in your UI. The JSON type and ID field names remain fixed English identifiers for receiving programs.

This example represents “a new device was activated.” It concerns license lic_01K456EXAMPLE and product prod_01K123EXAMPLE.
{
  "id": "evt_01K789EXAMPLE",
  "type": "activation.created",
  "api_version": "2026-08-24",
  "created_at": "2026-08-25T10:30:00.000+09:00",
  "tenant_id": "ten_01K000EXAMPLE",
  "data": {
    "activation_id": "act_01KABCEXAMPLE",
    "license_id": "lic_01K456EXAMPLE",
    "product_id": "prod_01K123EXAMPLE"
  }
}

api_version is the webhook payload version and is versioned separately from the API.

EventFixed fields for programs
When a new device is activatedUse this for support workflows and suspicious-use reviews.
Show identifiersactivation.createdactivation_id, license_id, product_id
When a device's connection source or information changesReview changes to its IP address, User-Agent, OS, app, or SDK. Notifications for the same device are limited to one per clock hour (UTC), and fields that changed in between are merged into the next one.
Show identifiersactivation.device_changedactivation_id, license_id, product_id, observation_id, changed_fields, observed_at
When a device activation is blocked by a risk checkReview automatic blocks caused by risks such as virtual machines or clock tampering.
Show identifiersactivation.risk_blockedactivation_id, license_id, observation_id, score, signals, action
When a license is issuedSync completed issuances to order management or CRM systems.
Show identifierslicense.createdlicense_id, product_id, customer_id
When a license expiresStart renewal outreach or follow-up access removal.
Show identifierslicense.expiredlicense_id, expired_at, reason
When a license is suspendedSync payment or support decisions to your systems.
Show identifierslicense.suspendedlicense_id, status, reason
When a license is resumedSend recovery notices or synchronize contract status.
Show identifierslicense.resumedlicense_id, status, reason
When a license is revokedRemove access after cancellation or update your records.
Show identifierslicense.revokedlicense_id, status, reason

Choose * (Send all events) to receive every event type, including types added later.A risk rule that stops a device sends activation.risk_blocked; one that suspends the license sends license.suspended.

customer_id is null when the license was issued without a customer. Ignore unknown fields so future additions remain compatible.