API version 2026-09-21
API referenceExternal 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
- Create an API key with only the required scopes
- Try issuing one license
- 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.
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:readand issue or change them withlicenses: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.
| Scope | What it allows |
|---|---|
products:read | View productsRetrieve registered product information. |
products:write | Manage productsCreate, update, and archive products. |
policies:read | View policiesRetrieve device limits, expiration rules, and other policy settings. |
policies:write | Manage policiesCreate and update license usage policies. |
customers:read | View customersRetrieve customer records. |
customers:write | Manage customersCreate and update customers, customer organizations, resellers and license users (not end-user sign-in settings). |
licenses:read | View licensesRetrieve license status and expiration information. |
licenses:write | Manage licensesIssue, update, suspend, resume, and revoke licenses. |
activations:delete | Deactivate devicesRemove lost or retired devices from a license. |
webhooks:read | View webhook settingsRetrieve registered webhook destinations. |
webhooks:write | Manage webhook settingsCreate, update, and disable webhook destinations. |
events:read | View audit eventsRetrieve administrative actions and device authentication events. |
reports:read | View usage reportsRetrieve license and device usage totals. |
sso:read | View sign-in settingsRetrieve the OIDC connections (identity providers) used to sign in to the customer portal. |
sso:write | Manage 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
expiryremains 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.
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_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_filesshows the status and the sealing key, andenable_product_sealed_filesturns 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
- Add the MCP server
https://usakey.jp/mcpto your AI assistant by giving it instructions or running the command below. - 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.
- 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/mcpThe 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:readpolicies:readcustomers:readlicenses:readevents:readreports:read- Allow day-to-day operations
- Also allow
customers:writelicenses:writeactivations:deleteto register customers, issue or change licenses, and deactivate devices. - Allow product, policy, and webhook configuration
- Also allow
products:writepolicies:writewebhooks:readandwebhooks:writeto 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:writeaccess 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:readand view OIDC connections. With “Allow product, policy, and webhook configuration” it also getssso:writeand 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.
- The tool argument
environment(testorlive). It appears only on connections with both and is not sent to the API. - The environment of the target named by an ID (license, then activation, policy, and product). When
environmentand the target's environment differ, Usakey stops without calling the API and returnsENVIRONMENT_REQUIREDwith a hint. - Lists without an ID (
list_products,list_policies,list_licenses,list_runtime_events) called withoutenvironmentrun on both environments, merge the results, and tag each item withenvironment. That uses two monthly calls. The next page comes from acursorthat keeps the position in each environment. - Creating a product (
create_product) requiresenvironment. Product names and codes are unique across test and live, so give the test and live copies different names and codes. - The usage report (
get_usage_report) needsenvironmentorproduct_id. Test and live numbers are not added together. - 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_licensecannot 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.
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.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, andMcp-Nameheaders, plusserver/discover) and the 2025-03-26 through 2025-11-25initializeflow - The
io.modelcontextprotocol/skillsextension (skills/list,skills/get,resources/directory/read) andskill://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), andiss(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
- Install Node.js 20.3 or later. No additional package is required.
- Download the official SDK, verify its checksum, and extract it. The server is under
usakey-sdk/mcp/. - 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.
- 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 --checkThe 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.mjsRun 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.
Add this to claude_desktop_config.json (Settings → Developer → Edit Config) and restart Claude Desktop. Use an absolute path.
{
"mcpServers": {
"usakey": {
"command": "node",
"args": ["/Users/<user-name>/usakey-sdk/mcp/bin/usakey-mcp.mjs"],
"env": {
"USAKEY_API_URL": "https://usakey.jp",
"USAKEY_MANAGEMENT_API_KEY_FILE": "/Users/<user-name>/.usakey/api-key"
}
}
}
}On Windows, write \ as \\. If it does not start, replace node with its absolute path. Usakey has not yet verified Claude Desktop behavior.
codex mcp add usakey \
--env USAKEY_API_URL=https://usakey.jp \
--env USAKEY_MANAGEMENT_API_KEY_FILE=$HOME/.usakey/api-key \
-- node $HOME/usakey-sdk/mcp/bin/usakey-mcp.mjsIt is registered when usakey appears in codex mcp list.
Choose API-key scopes
- Read status only
products:readpolicies:readcustomers:readlicenses:readevents:readreports:read. SetUSAKEY_MCP_READ_ONLY=trueto hide mutation tools.- Customers and licenses too
- Also allow
customers:writelicenses:writeactivations:deleteto register customers, issue or suspend licenses, and deactivate devices. - Products and policies too
- Also allow
products:writeandpolicies: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 keywebhooks:read/webhooks:writefor webhooks,customers:read/customers:writefor user-level licensing (resellers, customer organizations, groups, and license users), andsso:read/sso:writefor sign-in settings (OIDC connections).customers:writecannot manage OIDC connections.
Use the local server safely
- Do not auto-approve mutation tools. In particular,
revoke_licensecannot 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.
~/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.
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
- 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.
- Store the signing secret, shown only once, in a secure area of your server.
- Verify the signature against the body and headers exactly as received, before parsing the JSON.
- Record
X-Usakey-Event-Idto 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
endNode.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.
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.
| Event | Fixed 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.