Integration GuideAuthenticationEndpoint conventionsModelsErrorsWebhooks
1tokens1tokens
Get StartedAPI Reference

Webhooks

1tokens sends a signed POST request to the HTTPS URL you register in the console whenever a subscribed event happens. Create an endpoint on the Webhooks page and tick the events you want; new endpoints take effect within 30 seconds.

Event catalog

Model

TypeNamedata fields
model.listedModel listedmodel tenant_id previous_status status actor
model.delistedModel delistedmodel tenant_id previous_status status actor
model.deprecatedModel deprecatedmodel previous_status status actor
model.price.changedPrice changedmodel tenant_id pricing_status sale_price_mode multiplier price changes actor
model.user_price.changedUser price changedmodel user_id multiplier changes actor
model.updatedModel updatedmodel changes actor

API Key

TypeNamedata fields
api_key.createdKey createdapi_key actor
api_key.updatedKey updatedapi_key changes actor
api_key.revokedKey revokedapi_key previous_status status actor
api_key.expiredKey expiredapi_key actor
api_key.quota.exhaustedQuota exhaustedapi_key quota_limit_usd consumed_usd actor
api_key.rate_window.exceededWindow limit exceededapi_key window limit_usd consumed_usd actor

Account

TypeNamedata fields
account.balance.lowLow balanceaccount threshold_usd request_id actor
account.balance.exhaustedBalance exhaustedaccount reason request_id actor
account.credit.topupTop-upaccount amount_usd amount_credits source order_no actor
account.credit.adjustedCredit adjustedaccount delta_usd delta_credits kind reason actor
account.trial.grantedTrial grantedaccount amount_credits actor
account.disabledAccount disabledaccount previous_status status actor
account.enabledAccount enabledaccount previous_status status actor
account.password.resetPassword resetaccount actor

Order

TypeNamedata fields
order.createdOrder createdorder actor
order.paidOrder paidorder paid_at previous_status status actor
order.failedPayment failedorder reason previous_status status actor
order.redeemedCode redeemedorder code_masked actor

Subscription

TypeNamedata fields
subscription.startedSubscription startedsubscription previous_status status actor
subscription.renewedSubscription renewedsubscription actor
subscription.upgradedSubscription upgradedsubscription changes actor
subscription.cancelledSubscription cancelledsubscription previous_status status actor
subscription.payment_failedRenewal failedsubscription reason actor
subscription.quota.exhaustedQuota exhaustedsubscription request_id actor

Task

TypeNamedata fields
task.video.succeededVideo donetask video_count actor
task.video.failedVideo failedtask actor
task.generation.succeededGeneration donetask media output_count actor
task.generation.failedGeneration failedtask media actor

Content

TypeNamedata fields
content.changelog.publishedChangelog publishedentry actor
content.article.publishedArticle publishedarticle tenant_id previous_status status actor
content.article.unpublishedArticle unpublishedarticle tenant_id previous_status status actor

Webhook

TypeNamedata fields
webhook.endpoint.auto_disabledEndpoint auto-disabledendpoint reason consecutive_failures actor

Payload format

Every event shares one envelope: id (unique event id), type, domain, created_at (ISO 8601 UTC), api_version and data. Inside data keys are snake_case, timestamps end with _at, amounts are decimal strings (_usd / _credits) and absent values are explicit null. The same event may be delivered more than once because of retries, so deduplicate by id.

{  "id": "evt_01JBXK6Q3N7Y8R2V4W5X6Z7A8B",  "type": "model.price.changed",  "domain": "model",  "created_at": "2026-11-04T08:12:33Z",  "api_version": "2026-11-04",  "data": { }}

Request headers

POST https://your-server.example/webhooksContent-Type: application/jsonUser-Agent: Webhooks/1.0X-Event-Id: evt_01JBXK6Q3N7Y8R2V4W5X6Z7A8BX-Event-Type: model.price.changedX-Delivery-Id: 123456X-Timestamp: 1762243953X-Signature: v1=5f1c…

Verifying the signature

Compute HMAC-SHA256 with the secret shown when the endpoint was created over the string X-Timestamp value + . + raw request body, and compare it in constant time with the hex after v1= in X-Signature. Reject requests whose timestamp differs from now by more than 5 minutes. Respond 2xx first and process afterwards; the whole response must finish within 10 seconds.

import { createHmac, timingSafeEqual } from "node:crypto";export function verify(secret, headers, rawBody) {  const ts = headers["x-timestamp"];  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;  const expected = createHmac("sha256", secret).update(ts + "." + rawBody).digest("hex");  const given = String(headers["x-signature"]).replace(/^v1=/, "");  return given.length === expected.length && timingSafeEqual(Buffer.from(given, "hex"), Buffer.from(expected, "hex"));}
import hmac, hashlib, timedef verify(secret: str, headers: dict, raw_body: bytes) -> bool:    ts = headers["X-Timestamp"]    if abs(time.time() - int(ts)) > 300:        return False    expected = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()    given = headers["X-Signature"].removeprefix("v1=")    return hmac.compare_digest(given, expected)
func verify(secret string, ts, sig string, body []byte) bool {	t, _ := strconv.ParseInt(ts, 10, 64)	if math.Abs(float64(time.Now().Unix()-t)) > 300 {		return false	}	mac := hmac.New(sha256.New, []byte(secret))	mac.Write([]byte(ts + "."))	mac.Write(body)	expected := hex.EncodeToString(mac.Sum(nil))	return hmac.Equal([]byte(strings.TrimPrefix(sig, "v1=")), []byte(expected))}

Retries and auto-disable

A non-2xx response or no response within 10 seconds counts as a failure. Each delivery is attempted up to 7 times with the delays below; after that it is marked exhausted. An endpoint whose 10 consecutive deliveries are exhausted is disabled automatically and you are notified by email; re-enable it in the console once fixed. Any delivery can be re-sent manually from the console.

AttemptDelay after previous
10
21 min
35 min
430 min
52 h
66 h
724 h

Endpoint requirements

The URL must be a publicly reachable HTTPS address; hostnames that resolve to private, loopback or link-local ranges are rejected. Redirects are not followed and at most 64 KB of the response body is read.

Errors

Previous Page

On this page

Event catalogModelAPI KeyAccountOrderSubscriptionTaskContentWebhookPayload formatRequest headersVerifying the signatureRetries and auto-disableEndpoint requirements
{
  "id": "evt_01JBXK6Q3N7Y8R2V4W5X6Z7A8B",
  "type": "model.price.changed",
  "domain": "model",
  "created_at": "2026-11-04T08:12:33Z",
  "api_version": "2026-11-04",
  "data": { }
}
POST https://your-server.example/webhooks
Content-Type: application/json
User-Agent: Webhooks/1.0
X-Event-Id: evt_01JBXK6Q3N7Y8R2V4W5X6Z7A8B
X-Event-Type: model.price.changed
X-Delivery-Id: 123456
X-Timestamp: 1762243953
X-Signature: v1=5f1c…
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret, headers, rawBody) {
  const ts = headers["x-timestamp"];
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const expected = createHmac("sha256", secret).update(ts + "." + rawBody).digest("hex");
  const given = String(headers["x-signature"]).replace(/^v1=/, "");
  return given.length === expected.length && timingSafeEqual(Buffer.from(given, "hex"), Buffer.from(expected, "hex"));
}
import hmac, hashlib, time

def verify(secret: str, headers: dict, raw_body: bytes) -> bool:
    ts = headers["X-Timestamp"]
    if abs(time.time() - int(ts)) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
    given = headers["X-Signature"].removeprefix("v1=")
    return hmac.compare_digest(given, expected)
func verify(secret string, ts, sig string, body []byte) bool {
	t, _ := strconv.ParseInt(ts, 10, 64)
	if math.Abs(float64(time.Now().Unix()-t)) > 300 {
		return false
	}
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(ts + "."))
	mac.Write(body)
	expected := hex.EncodeToString(mac.Sum(nil))
	return hmac.Equal([]byte(strings.TrimPrefix(sig, "v1=")), []byte(expected))
}