See Authentication for how the returned tokens are used on every other endpoint.
Login
POST /api/v1/auth/login
Authenticates an existing user and returns a JWT access token (TTL 15 minutes) and a refresh token (TTL 7 days). The refresh token is stored in Redis; the access token is stateless, validated only by signature and expiration.
Auth: none
Body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | User email |
password | string | Yes | User password |
using var client = new HttpClient();
var response = await client.PostAsJsonAsync("https://api.hooksentry.com/api/v1/auth/login", new
{
email = "owner@example.com",
password = "SenhaSegura123!"
});
var auth = await response.Content.ReadFromJsonAsync<AuthResponse>();
record AuthResponse(string AccessToken, int ExpiresIn, string RefreshToken, DateTimeOffset ExpiresAt);
Return codes
200 OK— authentication successful400 Bad Request— missing required fields or invalid email format401 Unauthorized— invalid credentials or inactive user429 Too Many Requests— rate limit exceeded (see Errors & Rate Limiting)
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 900,
"refreshToken": "8f14e45fceea167a5a36dedd4bea2543...",
"expiresAt": "2026-07-10T14:32:00.000Z"
}
Refresh token
POST /api/v1/auth/refresh
Exchanges an active refresh token for a new token pair. The provided refresh token is consumed
atomically (single-use, via Redis GETDEL) — reusing it fails.
Auth: none
Body
| Field | Type | Required | Description |
|---|---|---|---|
refreshToken | string | Yes | Refresh token obtained at login or from a previous refresh |
using var client = new HttpClient();
var response = await client.PostAsJsonAsync("https://api.hooksentry.com/api/v1/auth/refresh", new
{
refreshToken
});
var auth = await response.Content.ReadFromJsonAsync<AuthResponse>();
Return codes
200 OK— new tokens issued successfully400 Bad Request— required field missing401 Unauthorized— invalid, expired, or already-used refresh token
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 900,
"refreshToken": "3c9d2b7e1a4f8c6d0e5b9a2f7c1d4e8b...",
"expiresAt": "2026-07-10T14:47:00.000Z"
}
Logout
POST /api/v1/auth/logout
Removes the refresh token from Redis and immediately revokes the access token via a Redis
denylist keyed by the token's jti. Any subsequent request with the same access token receives
401 Unauthorized.
Auth: Bearer token
Body
| Field | Type | Required | Description |
|---|---|---|---|
refreshToken | string | Yes | Refresh token to invalidate |
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.hooksentry.com/api/v1/auth/logout")
{
Content = JsonContent.Create(new { refreshToken })
};
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
using var client = new HttpClient();
var response = await client.SendAsync(request);
Return codes
204 No Content— logout successful400 Bad Request—refreshTokenmissing401 Unauthorized— missing/invalid JWT, or the refresh token doesn't belong to the caller