API & webhooks
Create secrets and requests, query the audit log, and receive signed webhooks from
your own tools. The API is available on the Business plan. The live, always-
current spec is at app.secretwolf.io/api-docs
(OpenAPI JSON at /api-docs/v1/openapi.json).
Building an AI agent? There's an anonymous, pay-per-use endpoint that needs no key — the agent pays $0.10 in USDC over x402 per secret. The reference is further down this page; the worked examples are on Pay-per-secret API for AI agents.
Base URL
https://app.secretwolf.io
Authentication
Send your API key as a Bearer token. Keys look like sw_live_… and are created in your
workspace settings. The key identifies your workspace; keep it secret and rotate it if exposed.
Authorization: Bearer sw_live_your_key_here
A missing or invalid key returns 401; a key whose workspace isn't on
the Business plan returns 403.
Create a secret
POST /api/v1/secrets
Body fields (all optional): text, isOneTimeUse (default true),
expiryHours, maxViews, notifyOnView, templateKey,
fields (a string-to-string map for templates), requireOtp,
recipientEmail, reference, ticketReference. The response is
metadata only — never the secret.
curl -X POST https://app.secretwolf.io/api/v1/secrets \
-H "Authorization: Bearer sw_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"text":"hunter2","isOneTimeUse":true,"expiryHours":24,"requireOtp":true,"recipientEmail":"[email protected]"}'
Returns:
{
"url": "https://acme.secretwolf.io/s/9fA2hTq7Kd",
"id": "9fA2hTq7Kd"
}
Same call in C#:
using System.Net.Http.Json;
using System.Net.Http.Headers;
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "sw_live_your_key_here");
var body = new
{
text = "hunter2",
isOneTimeUse = true,
expiryHours = 24,
requireOtp = true,
recipientEmail = "[email protected]"
};
var res = await http.PostAsJsonAsync($"{baseUrl}/api/v1/secrets", body);
res.EnsureSuccessStatusCode();
var created = await res.Content.ReadFromJsonAsync<CreatedSecret>();
Console.WriteLine(created!.Url);
record CreatedSecret(string Url, string Id);
Create a request
POST /api/v1/requests
Ask someone to send you a secret through a branded form. Fields: label
(required), templateKey, recipientEmail, expiryDays (default 7).
Requires the Team plan or higher.
curl -X POST https://app.secretwolf.io/api/v1/requests \
-H "Authorization: Bearer sw_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"label":"Your CMS admin login","recipientEmail":"[email protected]","expiryDays":7}'
Returns:
{
"id": "3f2e…",
"url": "https://acme.secretwolf.io/r/Vb7…",
"expiresAt": "2026-08-02T09:41:00Z"
}
Query the audit log
GET /api/v1/audit
Query parameters: type (an event type such as SecretViewed),
secretId, from, to, page (default 1),
pageSize (default 50, max 200). Metadata only — never the secret. Requires the Team
plan or higher.
curl "https://app.secretwolf.io/api/v1/audit?type=SecretViewed&page=1&pageSize=50" \
-H "Authorization: Bearer sw_live_your_key_here"
Returns:
{
"total": 128,
"page": 1,
"pageSize": 50,
"events": [
{
"createdAt": "2026-07-26T09:44:12Z",
"eventType": "SecretViewed",
"actor": "recipient",
"secretId": "9fA2hTq7Kd",
"recipientLabel": "[email protected]",
"ipAddress": "203.0.113.9",
"userAgent": "Mozilla/5.0 …"
}
]
}
Secret status
GET /api/v1/secrets/{id}/status
Returns the live status of a secret — active, or a terminal viewed,
expired or revoked once the payload is gone. Never returns the secret
itself.
Webhooks
Register an HTTPS endpoint and a whsec_… signing secret to receive events:
SecretViewed, SecretExpired and RequestFulfilled. Every
delivery is a POST with an X-SW-Event header and an
X-SW-Signature header, and is retried with backoff on failure. Payloads are metadata
only.
POST https://your-app.example.com/hooks/secretwolf
X-SW-Event: SecretViewed
X-SW-Signature: t=1753520652,v1=1b2c3d4e…
{
"event": "SecretViewed",
"occurredAt": "2026-07-26T09:44:12Z",
"data": { "secretId": "9fA2hTq7Kd" }
}
Verify the signature before trusting a delivery. The header is t={unix},v1={hex}, and the
signed value is {t}.{rawBody} using HMAC-SHA256 keyed by your signing secret:
// Signature header: X-SW-Signature: t={unixSeconds},v1={hexHmac}
// Signed value is "{t}.{rawBody}", HMAC-SHA256 keyed by your whsec_ signing secret.
static bool Verify(string signingSecret, string header, string rawBody)
{
var parts = header.Split(','); // ["t=…", "v1=…"]
var t = parts[0]["t=".Length..];
var sig = parts[1]["v1=".Length..];
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(signingSecret));
var expected = Convert.ToHexString(
hmac.ComputeHash(Encoding.UTF8.GetBytes(t + "." + rawBody))).ToLowerInvariant();
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(sig));
}
Rate limits
The v1 API allows 60 requests per minute per key. Over the limit returns
429 with a short plain-text body — back off and retry.
Errors
Validation and authentication errors return a JSON { "error": "…" } with a
400, 401, 403 or 404. Plan and processing
failures return an RFC 7807 application/problem+json body. Throttling returns a
plain-text 429.
Machine payments (x402)
A second, separate endpoint, with different rules from everything above: no API key, no account, and payment settled per call in USDC over the x402 protocol. Positioning and worked examples are on the AI agents page — this is the reference.
Create a secret
POST https://app.secretwolf.io/api/x402/secrets
Body is either text (up to 10,000 characters) or templateKey plus
fields, where templateKey is one of website-login,
dns-account, api-key or custom. Agent secrets are always
one-time-use, with no attachments and no recipient verification. A malformed body is rejected with
400 before any payment is taken, so a call that couldn't have produced
a secret is never charged. The response is the same { "url", "id" } shape as the v1
create endpoint; the plaintext is never returned and never logged.
The handshake
This endpoint speaks x402 version 2:
POSTwith no payment. The server answers402with the payment requirements base64-encoded in aPAYMENT-REQUIREDresponse header.- Your client signs a payment authorisation and retries the same
POSTwith it in aPAYMENT-SIGNATURErequest header. The legacy v1 nameX-PAYMENTis still accepted. - A facilitator verifies and settles on-chain; the server returns
200with the link, and the settlement receipt in aPAYMENT-RESPONSEheader.
The challenge offers one payment method: scheme exact, network
eip155:8453 (Base mainnet in CAIP-2 form), asset
USDC, at $0.10 per secret. Decoded, it looks like this:
{
"x402Version": 2,
"resource": {
"url": "https://app.secretwolf.io/api/x402/secrets",
"serviceName": "SecretWolf",
"tags": ["secret", "one-time", "credentials"]
},
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"amount": "100000",
"asset": "0x…",
"payTo": "0x…",
"maxTimeoutSeconds": 60,
"extra": { "name": "USD Coin", "version": "2" }
}
]
}
Read asset, payTo and extra from the live
challenge rather than hard-coding them. amount is in atomic USDC
units (6 decimals). extra is the EIP-712 domain you sign against and it comes from
the token contract, so its name is the contract's own — USD Coin, not
the USDC ticker used everywhere else on this page.
Consume a secret (agent to agent)
POST https://app.secretwolf.io/api/x402/secrets/{id}/reveal
Returns the value as JSON exactly once, then hard-deletes it — a second call is 404.
Free, because creation was already paid and the 256-bit id in the link is itself the capability, so
no key and no payment apply. It is POST-only by design: a bare GET can
never consume a secret, so a link scanner or preview bot can't burn one. Rate-limited per IP.
A free-text secret returns { "text": "…" }; a templated one returns
{ "templateKey", "fields" }.
Identifying your agent (optional)
If your agent implements Web Bot Auth, send the absolute https URL of your HTTP Message
Signatures key directory:
Signature-Agent: "https://agent.example.com"
We record it against the payment so a repeat caller stays recognisable across wallet addresses. It is
optional, it doesn't change the price, and nothing is gated on it — we don't verify the signature,
so it is a label rather than a credential. Anything that isn't an absolute https URL is
ignored.
Discovery and limits
The challenge carries discovery metadata (service SecretWolf, tags secret,
one-time, credentials) so x402-native agents can find the endpoint and read
its price without prior knowledge. Unpaid challenges are limited to 30 per minute per
IP — the payment itself is the real throttle. Settled payments are non-refundable; requests
that fail validation are never charged. Every agent-created link carries the "Powered by SecretWolf"
footer.
OpenAPI for the anonymous endpoints:
/openapi/x402.json — served from
this host so agents and crawlers can actually read it, and generated from the same constants as
the prices above.