Control API
Customer accounts and key controls through one API.
Access
Send a scoped reseller control key as a Bearer token to https://api.urouter.net/control/v1. Control keys cannot run inference, create additional control keys or add arbitrary credit.
Create a managed customer
Supply your own external_id. It’s unique within your integration, so repeating it returns the same customer. An email address or Telegram identity alone can’t claim an existing account.
curl https://api.urouter.net/control/v1/customers \
-H "Authorization: Bearer $UROUTER_CONTROL_KEY" \
-H "Content-Type: application/json" \
-d '{"external_id":"customer-8842"}'Link an existing account
POST /control/v1/link-requests with external_id to get a one-use approval_url. The account owner must sign in and approve the stated access. They can revoke it in Account, which also disables keys issued by your integration.
Create and manage inference keys
POST /control/v1/customers/{customerId}/keys accepts name, models, operations, lifetime_limit, daily_limit, monthly_limit, expires_at and mask_secrets. USD limits are decimal strings or null; expiry is ISO 8601 or null. GET lists metadata. PATCH changes only supplied fields. POST /keys/{keyId}/rotate replaces the secret while preserving the logical key. DELETE revokes it. See OpenAPI for exact bounds.
curl https://api.urouter.net/control/v1/customers/$CUSTOMER_ID/keys \
-H "Authorization: Bearer $UROUTER_CONTROL_KEY" \
-H "Idempotency-Key: $OPERATION_UUID" \
-H "Content-Type: application/json" \
-d '{"name":"App","daily_limit":"5","mask_secrets":false}'Retry without duplicating key changes
Use a new UUID Idempotency-Key for each intended key change. Reusing it returns HTTP 409 without repeating the change or showing the secret again. Check /control/v1/operations/{idempotencyKey}: applied includes resource_id. If you lost the secret, check the key’s metadata before rotating it with a new operation ID. Don’t blindly retry an operation that was accepted but hasn’t been applied.
Balance and usage
GET /control/v1/customers/{id} returns the exact USD balance. Filter /usage by model, state or inclusive UTC from/to dates, and use its opaque cursor for pagination. Totals cover all matching requests. Use /requests/{requestId} for one request owned by that customer. Inference keys can read only their own logical key’s requests at /v1/requests/{id}.
Signed events
GET /control/v1/events?cursor=0 returns up to 100 events and next_cursor. Verify HMAC-SHA256 using the control key and timestamp + a period + the exact response body. Compare the result with the sha256= digest in X-Urouter-Event-Signature. Reject a response if X-Urouter-Event-Timestamp differs from the current time by more than five minutes. Deduplicate by event id and save next_cursor. You can safely read a page again. Poll for events; outbound webhooks aren’t enabled.
Permissions
The control scopes are customers.read, customers.create, keys.read, keys.manage, usage.read and events.read. Revoking a token or integration removes access immediately. Key changes also check customer grants inside the write transaction.
Funding
Funding controls stay disabled until payment and usable-funds receipts are verified. There’s no arbitrary-credit endpoint, and customers aren’t given payment instructions yet. Balances can only pay for API usage.
Schema
/openapi.json covers released request fields, authentication, errors and lifecycle. Paid inference and funding aren’t listed as available.