Tenants

Create and manage HookSentry tenants — the isolated account every other resource belongs to.

Create Tenant

POST /api/v1/tenants

Creates a new tenant and its first Owner user in a single atomic operation. Automatically generates the webhookSecret (HMAC-SHA256) used to sign outbound webhooks.

Auth: none

Body

FieldTypeRequiredDescription
namestringYesUnique organization name
ownerEmailstringYesInitial owner user email — unique on the platform
ownerPasswordstringYesOwner password — stored as a hash
maxTrysintegerNo (default 10)Maximum delivery attempts before the circuit breaker opens
circuitBreakerTimerintegerNo (default 300)Seconds the circuit stays open before a half-open probe
deviceFingerprintstringNoBrowser fingerprint (FingerprintJS visitorId) — cloud only, ignored self-hosted
cfTurnstileTokenstringNoCloudflare Turnstile token — cloud only, ignored self-hosted
using var client = new HttpClient();

var response = await client.PostAsJsonAsync("https://api.hooksentry.com/api/v1/tenants", new
{
    name = "Acme Inc",
    ownerEmail = "owner@acme.com",
    ownerPassword = "SenhaSegura123!",
    maxTrys = 10,
    circuitBreakerTimer = 300
});

Return codes

  • 201 Created — tenant and owner created, includes the generated webhookSecret
  • 400 Bad Request — invalid data (malformed email, empty password)
  • 409 Conflict — a tenant with the same name already exists, or the email is already in use
  • 422 Unprocessable Entity — rejected by a registration guard, e.g. disposable email (cloud only)
  • 429 Too Many Requests — more than 5 requests from the same IP within 1 hour
{
  "tenantId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "tenantName": "Acme Inc",
  "webhookSecret": "whsec_5f8a3b2c1d9e4f6a7b8c9d0e1f2a3b4c",
  "maxTrys": 10,
  "circuitBreakerTimer": 300,
  "tenantCreatedAt": "2026-07-03T14:32:00.000Z",
  "ownerUserId": "9c858901-8a57-4791-81fe-4c455b099bc9",
  "ownerEmail": "owner@acme.com",
  "ownerRole": "Owner",
  "ownerCreatedAt": "2026-07-03T14:32:00.000Z"
}

Cloud-only anti-abuse fields:

deviceFingerprint and cfTurnstileToken are only enforced on HookSentry Cloud. Self-hosted deployments accept and ignore them.

Get Tenant by ID

GET /api/v1/tenants/{id}

Looks up a tenant by UUID. Never returns webhookSecret.

Auth: Bearer token

Path parameters

ParameterTypeDescription
idUUIDTenant UUID
var request = new HttpRequestMessage(HttpMethod.Get, $"https://api.hooksentry.com/api/v1/tenants/{tenantId}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

using var client = new HttpClient();
var response = await client.SendAsync(request);

Return codes

  • 200 OK — tenant data (without webhookSecret)
  • 401 Unauthorized — missing or invalid token
  • 404 Not Found — tenant not found
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "Acme Inc",
  "maxTrys": 10,
  "circuitBreakerTimer": 300,
  "createdAt": "2026-07-03T14:32:00.000Z",
  "updatedAt": "2026-07-03T14:32:00.000Z"
}

Update Tenant

PATCH /api/v1/tenants/{id}

Partially updates the tenant's name and webhook delivery settings. Only Admin or Owner users may call this endpoint, and only for their own tenant.

Auth: Bearer token — Admin or Owner role

Path parameters

ParameterTypeDescription
idUUIDTenant UUID — must match the caller's tenant

Body (all fields optional)

FieldTypeDescription
namestringNew unique tenant name
maxTrysintegerMax delivery attempts before opening the circuit breaker (min 1)
circuitBreakerTimerintegerSeconds the circuit stays open before a half-open probe (min 1)
var request = new HttpRequestMessage(HttpMethod.Patch, $"https://api.hooksentry.com/api/v1/tenants/{tenantId}")
{
    Content = JsonContent.Create(new { maxTrys = 15 })
};
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

using var client = new HttpClient();
var response = await client.SendAsync(request);

Return codes

  • 200 OK — tenant updated
  • 400 Bad Request — invalid value
  • 401 Unauthorized — missing or invalid token
  • 403 Forbidden — caller is not Admin/Owner, or the tenant belongs to another account
  • 404 Not Found — tenant not found
  • 409 Conflict — name already taken by another tenant
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "Acme Inc",
  "maxTrys": 15,
  "circuitBreakerTimer": 300,
  "createdAt": "2026-07-03T14:32:00.000Z",
  "updatedAt": "2026-07-03T15:10:00.000Z"
}

Get Webhook Secret

GET /api/v1/tenants/{id}/webhook-secret

Returns the HMAC-SHA256 secret used to sign outbound webhooks delivered to destination URLs.

Auth: Bearer token — caller's own tenant only

var request = new HttpRequestMessage(HttpMethod.Get,
    $"https://api.hooksentry.com/api/v1/tenants/{tenantId}/webhook-secret");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

using var client = new HttpClient();
var response = await client.SendAsync(request);

Return codes

  • 200 OK — webhook secret
  • 401 Unauthorized — missing or invalid token
  • 403 Forbidden — tenant belongs to another user
  • 404 Not Found — tenant not found
{
  "webhookSecret": "whsec_5f8a3b2c1d9e4f6a7b8c9d0e1f2a3b4c"
}

Regenerate Webhook Secret

POST /api/v1/tenants/{id}/webhook-secret

Generates a new HMAC-SHA256 webhook secret and invalidates the previous one. All destination servers must be updated with the new secret to keep verifying the X-HookSentry-Signature header on incoming webhooks.

Auth: Bearer token — caller's own tenant only

var request = new HttpRequestMessage(HttpMethod.Post,
    $"https://api.hooksentry.com/api/v1/tenants/{tenantId}/webhook-secret");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

using var client = new HttpClient();
var response = await client.SendAsync(request);

Return codes

  • 200 OK — new webhook secret
  • 401 Unauthorized — missing or invalid token
  • 403 Forbidden — tenant belongs to another user
  • 404 Not Found — tenant not found
{
  "webhookSecret": "whsec_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
}

Verify Webhook Signature

POST /api/v1/tenants/{id}/webhook-secret/verify

Computes HMAC-SHA256 of the provided payload using the tenant's webhook secret and compares it (timing-safe) against the provided signature. Useful for testing your X-HookSentry-Signature verification logic without waiting for a real delivery.

Auth: Bearer token — caller's own tenant only

Body

FieldTypeRequiredDescription
payloadstringYesRaw JSON string that was signed
signaturestringYesValue of the X-HookSentry-Signature header, e.g. sha256=abc123...
var request = new HttpRequestMessage(HttpMethod.Post,
    $"https://api.hooksentry.com/api/v1/tenants/{tenantId}/webhook-secret/verify")
{
    Content = JsonContent.Create(new { payload = rawBody, signature = headerValue })
};
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

using var client = new HttpClient();
var response = await client.SendAsync(request);

Return codes

  • 200 OK{ "valid": true | false }
  • 401 Unauthorized — missing or invalid token
  • 403 Forbidden — tenant belongs to another user
  • 404 Not Found — tenant not found
{
  "valid": true
}