Webhooks, rate limits, and project keys
The control-plane knobs that govern how a project is exposed to the outside world. All endpoints in this page are JWT-only — a leaked jg_p_* key must not be able to disable rate limits, redirect webhooks, or mint sibling keys.
Project settings
GET /api/projects/{id}/settings
Read the current settings. The webhook secret is never returned (only its 13-char prefix and a boolean indicating whether it's set).
Auth: Owner JWT.
GET /api/projects/3a7b5c1d-.../settings
Authorization: Bearer eyJhbGc...
{
"settings": {
"webhook_url": "https://api.partner.example/juglans/events",
"webhook_secret_prefix": "whsec_a1b2c3d",
"webhook_secret_set": true,
"rate_limit_rpm": 120
}
}
PATCH /api/projects/{id}/settings
Partial update. Fields not present are left alone. Pass empty string to clear webhook_url; pass 0 to clear rate_limit_rpm.
Auth: Owner JWT.
PATCH /api/projects/3a7b5c1d-.../settings
Authorization: Bearer eyJhbGc...
Content-Type: application/json
{
"webhook_url": "https://api.partner.example/juglans/events",
"rate_limit_rpm": 120
}
{
"settings": {
"webhook_url": "https://api.partner.example/juglans/events",
"webhook_secret_prefix": "whsec_a1b2c3d",
"webhook_secret_set": true,
"rate_limit_rpm": 120
}
}
webhook_url must start with http:// or https://. Pass 0 or any non-positive rate_limit_rpm to clear the limit — the value is coerced to NULL server-side rather than stored.
POST /api/projects/{id}/settings/webhook/rotate-secret
Generate a fresh whsec_* signing secret. The plaintext is returned once; afterwards only the hash + prefix is retrievable.
Auth: Owner JWT.
curl -X POST https://api.juglans.ai/api/projects/3a7b5c1d-.../settings/webhook/rotate-secret \
-H "Authorization: Bearer eyJhbGc..."
{
"settings": {
"webhook_url": "https://api.partner.example/juglans/events",
"webhook_secret_prefix": "whsec_e5f6a7b",
"webhook_secret_set": true,
"rate_limit_rpm": 120
},
"secret": "whsec_e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6"
}
Use this secret to verify HMAC-SHA-256 signatures on incoming webhook bodies.
Honest status: webhook configuration storage is implemented end-to-end (settings round-trip, secret hashing, rotation). The asynchronous event delivery — actually emitting POSTs to your URL when a chat completes or a tool fires — is on the roadmap. Setting these fields today reserves the slot; events will start flowing once delivery ships. The signing-secret format will not change.
Rate limits
rate_limit_rpm is a fixed-window-per-minute ceiling on POST /api/projects/{id}/chat. The window is per-project (not per-key, not per-end-user). Exceeding the limit returns 429 with a body indicating retry timing:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{
"error": "rate limit exceeded",
"retry_after_seconds": 23,
"limit_rpm": 120
}
NULL (or 0 on PATCH) means unlimited. There is no per-route configuration — the limit only applies to chat.
Project API keys
Mint, list, and revoke jg_p_* keys. JWT-only by design: keys cannot manage themselves (a leaked key could otherwise rotate itself and lock out the owner).
POST /api/projects/{id}/api-keys
Mint a new key. The plaintext is returned once.
Auth: Owner JWT.
POST /api/projects/3a7b5c1d-.../api-keys
Authorization: Bearer eyJhbGc...
Content-Type: application/json
{ "name": "production backend" }
{
"api_key": {
"id": "9a8b7c6d-...",
"project_id": "3a7b5c1d-...",
"name": "production backend",
"prefix": "jg_p_a1b2c3d",
"created_at": "2026-04-29T10:00:00Z",
"key": "jg_p_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1"
}
}
Surface key to the operator once and never persist it. After this response, only prefix (the first 12 characters) is retrievable.
GET /api/projects/{id}/api-keys
List active (unrevoked) keys.
Auth: Owner JWT.
{
"api_keys": [
{
"id": "9a8b7c6d-...",
"name": "production backend",
"prefix": "jg_p_a1b2c3d",
"created_at": "2026-04-29T10:00:00Z",
"last_used_at": "2026-04-29T11:32:14Z"
}
]
}
last_used_at is best-effort touched on every successful authentication.
DELETE /api/projects/{id}/api-keys/{key_id}
Revoke a key. 204 No Content. Effective immediately — subsequent calls bearing the key fail with 401.
Auth: Owner JWT.
curl -X DELETE https://api.juglans.ai/api/projects/3a7b5c1d-.../api-keys/9a8b7c6d-... \
-H "Authorization: Bearer eyJhbGc..."