Create a foreground tunnel or webhook endpoint. Send an OtterKit token in the Authorization: Bearer otk_… header; get one with otterkit login or from the console. The call charges 1 credit for the first hour and returns the session details. Each further hour charges 1 credit, and no endpoint bills more than 300 credits per rolling 30 days. All API routes live on https://otterkit.app.
POST
/api/agent/tunnelsQuery parameters#
| Parameter | Required | Values |
|---|---|---|
ttl | No (default 24h) | Auto-stop, e.g. 45m, 4h, 3d, or "never" |
subdomain | No | A stable name to reserve or reuse; omit for a random one |
kind | No | tunnel or webhook; picks the random-name prefix |
email | No | 1 to give a webhook endpoint an email inbox |
Response#
200 OK:
{
"subdomain": "tunnel-a1b2c3d4",
"publicUrl": "https://tunnel-a1b2c3d4.otterkit.app",
"connectUrl": "wss://otterkit.app/ws/connect?subdomain=tunnel-a1b2c3d4&token=<token>",
"mode": "foreground",
"pricing": {
"hourlyCredits": 1,
"monthlyCapCredits": 300,
"note": "Charged per connected hour, never more than 300 credits per 30 days; first hour charged now."
},
"autoStopAt": "2026-10-03T09:00:00.000Z",
"cost": 1,
"balance": 49
}Keep the token from connectUrl. You need it to connect the client and to deprovision the session. Webhook endpoints created with email=1 also return an emailAddress.
Errors#
| Status | Body | Meaning |
|---|---|---|
400 | { "error": "invalid_subdomain" } | The name is malformed or reserved, or the TTL is invalid |
401 | { "error": "authentication_required" } | No token was sent (invalid_token if it was rejected) |
402 | { "error": "insufficient_credits", "balance": 0, "required": 1, "topUpUrl": "…" } | Not enough credits |
409 | { "error": "subdomain_taken" } | The name belongs to someone else, is in use, or you hit the reservation limit |