Seller Integration Guide
Everything you need to accept subscription tokens, report usage, handle token rotation, and operate a reliable service on Crochet.
1. Quick Start (Verify-First)
When a buyer calls your endpoint, they include a subscription token (prefixed cro_sub_). Before serving the request, hash the token and verify it with Crochet.
Step A: Hash the incoming token
Compute the SHA-256 hash of the raw token. Never send the raw token over the wire.
import crypto from "crypto";
function hashToken(raw: string): string {
return crypto.createHash("sha256").update(raw).digest("hex");
}import hashlib
def hash_token(raw: str) -> str:
return hashlib.sha256(raw.encode()).hexdigest()Step B: Verify with Crochet
curl -X POST https://getcrochet.ai/api/v1/subscriptions/tokens/verify \
-H "Authorization: Bearer am_k_YOUR_SELLER_KEY" \
-H "Content-Type: application/json" \
-d '{
"token_hash": "a1b2c3d4e5f6..."
}'Valid token
{
"valid": true,
"data": {
"listing_id": "lst_...",
"status": "active",
"usage_count": 47,
"usage_limit": 100,
"remaining": 53,
"expires_at": "2026-04-17T00:00:00Z",
"subscriber_id": "usr_..."
}
}Invalid / unknown token
{
"valid": false
}{ valid: false } with no extra details, so you can't enumerate other sellers' tokens.Step C: Serve or reject
async function handleRequest(req) {
const token = req.headers["x-subscription-token"];
if (!token?.startsWith("cro_sub_")) return respond(401);
const hash = hashToken(token);
const res = await verifyToken(hash);
if (!res.valid) return respond(403, "Invalid token");
if (res.data.remaining === 0) return respond(429, "Usage limit reached");
// Serve the request
const result = await processRequest(req.body);
// Report usage (fire-and-forget)
reportUsage(hash, 1).catch(() => {});
return respond(200, result);
}2. Scaling Up (Polling)
Calling /tokens/verify on every request adds latency. For high-throughput services, cache the verification result and poll periodically.
Recommended caching strategy
| Volume | Strategy | Cache TTL |
|---|---|---|
| < 1 req/sec | Verify every request | None |
| 1-50 req/sec | Cache + poll every 60s | 60 seconds |
| 50+ req/sec | Cache + poll every 30s | 30 seconds |
const tokenCache = new Map<string, { valid: boolean; expiresAt: number }>();
async function isTokenValid(hash: string): Promise<boolean> {
const cached = tokenCache.get(hash);
if (cached && Date.now() < cached.expiresAt) {
return cached.valid;
}
const res = await verifyToken(hash);
tokenCache.set(hash, {
valid: res.valid,
expiresAt: Date.now() + 60_000, // 60s TTL
});
return res.valid;
}remaining field. If remaining is 0 and you continue serving, usage reports will fail and your Crochet Score may drop.Rate limits
The verify endpoint allows 300 requests/minute per seller. The usage endpoint also allows 300 req/min. Both are more than enough for polling-based architectures.
3. Usage Reporting
After serving a request, report usage so the buyer's usage count stays accurate. Usage is incremented atomically on the server.
curl -X POST https://getcrochet.ai/api/v1/subscriptions/tokens/usage \
-H "Authorization: Bearer am_k_YOUR_SELLER_KEY" \
-H "Content-Type: application/json" \
-d '{
"token_hash": "a1b2c3d4e5f6...",
"count": 1
}'{
"success": true,
"data": {
"token_id": "tok_...",
"usage_count": 48,
"usage_limit": 100,
"remaining": 52,
"status": "active"
}
}count field
Integer between 1 and 1000. Use higher values for batch operations (e.g. count: 10 for a batch of 10 items).
Auto-expiry
When usage_count reaches usage_limit, the token status changes to expired automatically.
Batching usage reports
If you serve many requests per second, buffer usage locally and report in batches. For example, accumulate counts over 10 seconds and send a single report with count: 50.
const pendingUsage = new Map<string, number>();
function recordUsage(tokenHash: string, count = 1) {
pendingUsage.set(tokenHash, (pendingUsage.get(tokenHash) ?? 0) + count);
}
// Flush every 10 seconds
setInterval(async () => {
for (const [hash, count] of pendingUsage.entries()) {
await reportUsage(hash, count).catch(() => {});
pendingUsage.delete(hash);
}
}, 10_000);4. Token Lifecycle
Subscription tokens move through a defined set of states. Understanding the lifecycle helps you handle edge cases gracefully.
| Status | Meaning | Your Action |
|---|---|---|
| active | Token is valid and under usage limit | Serve the request |
| expired | Past expiration date or usage limit reached | Reject with 403 |
| revoked | Buyer rotated to a new token | Reject with 403 |
Token rotation
When a buyer rotates their token (e.g. after a compromise), the old token's status becomes revoked. The next request will arrive with a new token hash. Your verify call will return valid: false for the old hash and valid: true for the new one.
Handling transitions in code
async function handleRequest(req) {
const hash = hashToken(req.token);
const res = await verifyToken(hash);
if (!res.valid) {
// Token is expired, revoked, or unknown
return respond(403, {
error: "TOKEN_INVALID",
message: "Subscription token is not valid. It may have been rotated or expired."
});
}
if (res.data.remaining !== null && res.data.remaining <= 0) {
return respond(429, {
error: "USAGE_LIMIT_REACHED",
message: "Usage limit exhausted. Subscriber must upgrade or renew."
});
}
// Serve + report
const result = await processRequest(req.body);
reportUsage(hash, 1).catch(() => {});
return respond(200, result);
}5. Error Code Reference
HTTP status codes and error bodies returned by the seller-facing endpoints. All error responses include a success: false field and an error object.
{
"success": false,
"error": {
"code": "BAD_REQUEST",
"message": "token_hash is required and must be a non-empty string"
}
}| Status | Code | When | Fix |
|---|---|---|---|
| 400 | BAD_REQUEST | Missing or invalid fields in request body | Check required fields and types |
| 401 | UNAUTHORIZED | Missing or invalid API key | Include valid Bearer am_k_... header |
| 403 | FORBIDDEN | Token belongs to a different seller's listing | You can only verify tokens for your own listings |
| 404 | NOT_FOUND | Token hash not found in database | Token may be misformatted or the subscription was cancelled |
| 429 | RATE_LIMITED | Over 300 requests/minute | Back off; use caching or batch usage reports |
| 500 | INTERNAL_ERROR | Server-side issue | Retry with exponential backoff |
Rate limit headers
When rate-limited, the response includes a Retry-After header with the number of seconds to wait before retrying.
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json6. Best Practices
Always verify before serving
Never serve data based on a token you haven't verified. Even with caching, make sure every token has been verified at least once.
Fire-and-forget usage reports
Don't block your response on usage reporting. Send usage asynchronously and handle failures silently. A slightly delayed count is better than increased latency.
Handle token rotation gracefully
When a cached token suddenly fails verification, don't panic. The buyer likely rotated their token. Clear the cache entry and re-verify the new token on the next request.
Batch usage at high volume
At 50+ req/sec, accumulate usage counts locally and flush every 10-30 seconds. This keeps you well under the 300 req/min rate limit.
Recommended error responses
When rejecting requests from buyers, use consistent error codes so their agents can handle failures programmatically.
| Situation | Status | Suggested error |
|---|---|---|
| No token header | 401 | MISSING_TOKEN |
| Token fails verification | 403 | TOKEN_INVALID |
| Usage limit exhausted | 429 | USAGE_LIMIT_REACHED |
| Token expired | 403 | TOKEN_EXPIRED |
Seller Endpoints Summary
| Method | Path | Purpose | Rate Limit |
|---|---|---|---|
| POST | /subscriptions/tokens/verify | Check if a token is valid | 300/min |
| POST | /subscriptions/tokens/usage | Report usage after serving | 300/min |
| POST | /subscriptions/tokens/rotate | Rotate a compromised token (buyer) | 10/min |
All paths are relative to https://getcrochet.ai/api/v1. See the full API reference for every endpoint.
What's Next?
You're set up to verify tokens, report usage, and handle edge cases. Here are some next steps.