Kiges ID API
Complete REST API for integrating Kiges ID authentication and organization management into your SaaS application. Allows your users to authenticate via Kiges ID, select organizations, manage members, roles, and more.
Overview
The Kiges ID API is organized around REST. It uses OAuth 2.0 Bearer tokens for authentication, returns JSON responses, and uses standard HTTP response codes.
Base URL
https://id.kiges.cc/api
Authentication
OAuth 2.0 Bearer token via Authorization header
Content Type
application/json for all request and response bodies
OIDC Discovery
/.well-known/openid-configuration
Authentication
All API endpoints require an OAuth 2.0 access token obtained through the Authorization Code flow with PKCE. Include the token in the Authorization header.
Getting an Access Token
1. Redirect the user to the authorization endpoint:
https://id.kiges.cc/oauth/authorize
2. After the user authorizes, exchange the code at the token endpoint:
https://id.kiges.cc/api/oauth/token
POST https://id.kiges.cc/api/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&redirect_uri=https://yourapp.com/callback
&code=AUTHORIZATION_CODE
&code_verifier=PKCE_CODE_VERIFIER
{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "eyJ0eXAiOiJKV1Q...",
"refresh_token": "def502003b4e..."
}
Using the Token
Authorization: Bearer eyJ0eXAiOiJKV1Q...
Scopes
Request specific scopes during the OAuth authorization to control what data your application can access. Include scopes as a space-separated list in the scope parameter.
| Scope | Description | Grants Access To |
|---|---|---|
openid | OpenID Connect | User's unique stable identifier (sub) |
profile | User profile | name, surname, birthday, gender, phone, language |
email | Email address | email, email_verified |
organizations | Organization list | User's organization memberships in /user and /userinfo |
org:read | Read org data | Organization API read endpoints (list, show, members, roles) |
org:manage | Manage org | Organization API write endpoints (create, update, delete, invite) |
openid profile email organizations org:read
Add
org:manage only if your SaaS needs to create/modify organizations or members on behalf of the user.
https://id.kiges.cc/oauth/authorize?
client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&response_type=code
&scope=openid profile email organizations org:read
&state=random_csrf_token
&code_challenge=PKCE_CHALLENGE
&code_challenge_method=S256
Error Handling
The API uses standard HTTP status codes and returns error details in JSON format.
| Code | Meaning | Description |
|---|---|---|
200 | OK | Request succeeded |
201 | Created | Resource was created |
204 | No Content | Successful deletion, no response body |
400 | Bad Request | Invalid request parameters |
401 | Unauthorized | Missing or invalid access token |
403 | Forbidden | Insufficient scope or organization permission |
404 | Not Found | Resource does not exist |
409 | Conflict | Resource already exists (duplicate member, etc.) |
422 | Unprocessable | Validation error — see errors field |
429 | Too Many Requests | Rate limit exceeded |
{
"error": "insufficient_scope",
"message": "The access token does not have the 'org:read' scope."
}
// Validation Error (422)
{
"message": "The given data was invalid.",
"errors": {
"name": ["The name field is required."]
}
}
Rate Limits
API requests are rate-limited to prevent abuse. The default limit is 60 requests per minute per token. Rate limit information is returned in response headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per window |
X-RateLimit-Remaining | Requests remaining in current window |
Retry-After | Seconds until the rate limit resets (only on 429) |
User Context
GET /api/v1/me
Returns the authenticated user's profile along with their organization memberships, roles, and permissions. This is the primary endpoint your SaaS should call after authentication to get everything needed to set up the user's session.
{
"data": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"name": "John",
"surname": "Doe",
"email": "john@example.com",
"email_verified": true,
"organizations": [
{
"uuid": "org-uuid-1",
"name": "Acme Corp",
"slug": "acme-corp",
"role": "admin",
"permissions": ["manage_members", "manage_roles", "invite_members", ...],
"members_count": 42
}
]
}
}
GET /api/user
Returns user profile fields based on granted scopes. Lighter than /v1/me — returns only what your scopes allow.
Fields returned depend on scopes: profile returns name, surname, email, birthday, gender, phone, language. organizations / org:read adds the organizations array with role and permissions.
GET /api/userinfo
Standard OpenID Connect UserInfo endpoint. Returns claims based on granted scopes using OIDC standard field names (given_name, family_name, email_verified, etc.).
Organizations
List Organizations
Returns all organizations the authenticated user belongs to.
{
"data": [
{
"uuid": "550e8400-...",
"name": "Acme Corp",
"slug": "acme-corp",
"description": "Our company workspace",
"is_active": true,
"members_count": 42,
"role": "admin",
"permissions": ["manage_members", "view_members", ...],
"created_at": "2026-01-15T10:30:00+00:00"
}
]
}
Create Organization
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Required | Organization name (max 255 chars) |
description | string | Optional | Description (max 1000 chars) |
{
"data": {
"uuid": "new-org-uuid",
"name": "My Organization",
"slug": "my-organization",
"description": "A workspace for my team",
"created_at": "2026-03-19T12:00:00+00:00"
}
}
Get Organization
Returns full organization details including roles, members, and your permissions. Requires membership.
Path Parameters
| Param | Description |
|---|---|
uuid | Organization UUID |
Update Organization
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Optional | New name |
description | string | Optional | New description |
Delete Organization
Permanently deletes an organization. Only the owner can perform this action.
Returns 204 No Content on success.
Members
List Members
{
"data": [
{
"user_uuid": "user-uuid-1",
"email": "john@example.com",
"name": "John",
"surname": "Doe",
"role": "admin",
"role_display_name": "Admin",
"permissions": ["manage_members", ...],
"joined_at": "2026-01-20T09:00:00+00:00"
}
]
}
Get Single Member
Returns the specified member's profile, role, and permissions within the organization.
Add Member
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Required | Email of a registered Kiges ID user |
role | string | Optional | Role name to assign (default: member) |
Returns 201 with the new member data. Returns 404 if user not found, 409 if already a member.
Update Member Role
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
role | string | Required | New role name for the member |
Remove Member
Returns 204 No Content on success. The organization owner cannot be removed.
Roles
List Roles
{
"data": [
{
"id": 1,
"name": "owner",
"display_name": "Owner",
"permissions": ["manage_organization", "manage_members", ...],
"is_system": true,
"is_default": false,
"members_count": 1
}
]
}
Default System Roles
| Role | Permissions |
|---|---|
owner | All permissions |
admin | All except manage_organization |
member | view_members, invite_members |
viewer | view_members |
Available Permissions
| Permission | Description |
|---|---|
manage_organization | Update org name, description, settings |
manage_members | Add members, change roles |
manage_roles | Create, edit, delete custom roles |
invite_members | Send and manage invitations |
remove_members | Remove members from the organization |
view_members | View the member list |
view_audit_logs | Access the audit log |
manage_sso | Configure SAML SSO settings |
manage_scim | Configure SCIM provisioning |
manage_billing | Manage billing and subscriptions |
Get Role
Create Custom Role
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Required | Unique role identifier (will be slugified) |
display_name | string | Required | Human-readable role name |
permissions | string[] | Required | Array of permission strings |
{
"name": "project-manager",
"display_name": "Project Manager",
"permissions": ["manage_members", "invite_members", "view_members", "view_audit_logs"]
}
Update Role
Delete Role
Returns 204 No Content on success. Cannot delete system roles or roles with assigned members.
Invitations
List Invitations
{
"data": [
{
"uuid": "inv-uuid-1",
"email": "jane@example.com",
"role": "member",
"role_display_name": "Member",
"status": "pending",
"invited_by": "John Doe",
"expires_at": "2026-03-26T12:00:00+00:00",
"created_at": "2026-03-19T12:00:00+00:00"
}
]
}
Create Invitation
Sends an email invitation to join the organization. If the email belongs to a registered Kiges ID user, they'll receive a clickable link. The invitation expires after 7 days.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Required | Email address to invite |
role | string | Optional | Role to assign (default: member) |
Cancel Invitation
Only pending invitations can be cancelled.
Permissions & Audit
Check Permissions
Check the authenticated user's permissions within an organization. Useful for your SaaS to determine what UI elements or actions to show.
Query Parameters
| Param | Type | Description |
|---|---|---|
permissions | string | Comma-separated list of permissions to check (e.g., manage_members,invite_members) |
// GET /api/v1/organizations/{uuid}/permissions?permissions=manage_members,manage_billing
{
"data": {
"is_member": true,
"role": "admin",
"permissions": ["manage_members", "manage_roles", "invite_members", ...],
"checked": {
"manage_members": true,
"manage_billing": false
}
}
}
Audit Logs
Retrieve the organization's audit log. Supports filtering and pagination.
Query Parameters
| Param | Type | Description |
|---|---|---|
event | string | Filter by event type (e.g., member.added) |
from | date | Start date (YYYY-MM-DD) |
to | date | End date (YYYY-MM-DD) |
per_page | integer | Items per page (max 100, default 25) |
page | integer | Page number |
Organization Notifications
Route signed events from Kiges services to organization-owned destinations: Telegram, Slack, Discord, Google Chat, Microsoft Teams, generic webhooks, and email. Deliveries are queued on the notifications queue with automatic retries.
7 channels
Telegram, Slack, Discord, Google Chat, Teams, Webhook, Email
Routing rules
Filter by service, team, event wildcard, and minimum severity
Service dispatch
HMAC-signed POST /api/v1/notifications/dispatch for Kiges services
Management API
OAuth-authenticated CRUD for destinations, rules, and delivery logs
manage_notifications in the target organization. See ORGANIZATION_NOTIFICATIONS.md for full setup and payload details.
Service Event Dispatch
Kiges services dispatch signed notification events. No user OAuth token — use service HMAC headers instead.
Headers
| Header | Description |
|---|---|
X-Kiges-Service | Service code (e.g. kiges-pay) |
X-Kiges-Timestamp | Unix timestamp |
X-Kiges-Signature-256 | sha256=<hmac> over timestamp.raw_body |
Body
| Field | Type | Description |
|---|---|---|
organization_uuid | uuid required | Target organization |
service_code | string required | Must match X-Kiges-Service |
event | string required | Event name (supports wildcard rules) |
severity | string | debug|info|warning|error|critical |
title | string required | Short headline |
message | string required | Human-readable body |
url | url | Optional deep link |
metadata | object | Optional structured data |
Returns 202 Accepted with matched_destinations and delivery_ids.
Notification Destinations
Create a destination. Supported type values: telegram, slack, discord, google_chat, microsoft_teams, webhook, email. Webhook-based types require webhook_url; email requires emails; generic webhook accepts optional webhook_secret.
Regenerate a Telegram /kiges_connect token.
Also: GET/PUT/DELETE .../notification-endpoints/{endpointUuid}. Discover channel metadata via GET /api/v1/notification-channel-types.
Notification Rules
Body: name, notification_endpoint_uuid, optional organization_team_id, saas_service_id, events array, minimum_severity.
Notification Deliveries
Paginated delivery log. Filter with status and event. Test routing via POST /api/v1/organizations/{uuid}/notifications/test.
Integration Guide
Complete OAuth 2.0 Flow
Here is the full flow for integrating Kiges ID into your SaaS application.
// Generate PKCE challenge
async function generatePKCE() {
const verifier = crypto.randomUUID() + crypto.randomUUID();
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hash = await crypto.subtle.digest('SHA-256', data);
const challenge = btoa(String.fromCharCode(...new Uint8Array(hash)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
return { verifier, challenge };
}
const { verifier, challenge } = await generatePKCE();
sessionStorage.setItem('pkce_verifier', verifier);
const authUrl = new URL('https://id.kiges.cc/oauth/authorize');
authUrl.searchParams.set('client_id', 'YOUR_CLIENT_ID');
authUrl.searchParams.set('redirect_uri', 'https://yourapp.com/callback');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'openid profile email organizations org:read');
authUrl.searchParams.set('state', crypto.randomUUID());
authUrl.searchParams.set('code_challenge', challenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
window.location.href = authUrl.toString();
// routes/web.php callback handler
Route::get('/callback', function (Request $request) {
// Exchange authorization code for access token
$response = Http::asForm()->post('https://id.kiges.cc/api/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => config('services.kiges.client_id'),
'client_secret' => config('services.kiges.client_secret'),
'redirect_uri' => config('services.kiges.redirect'),
'code' => $request->code,
]);
if (!$response->ok()) {
return redirect('/login')->withErrors('Authentication failed');
}
$tokens = $response->json();
$accessToken = $tokens['access_token'];
// Fetch user profile + organizations in one call
$userData = Http::withToken($accessToken)
->get('https://id.kiges.cc/api/v1/me')
->json('data');
// Create or update user in your database
$user = User::updateOrCreate(
['kiges_uuid' => $userData['uuid']],
[
'name' => $userData['name'],
'surname' => $userData['surname'],
'email' => $userData['email'],
'kiges_token' => $accessToken,
'kiges_refresh' => $tokens['refresh_token'],
]
);
// Sync organizations
foreach ($userData['organizations'] ?? [] as $orgData) {
$org = Organization::updateOrCreate(
['kiges_uuid' => $orgData['uuid']],
[
'name' => $orgData['name'],
'slug' => $orgData['slug'],
]
);
$org->members()->updateOrCreate(
['user_id' => $user->id],
['role' => $orgData['role'], 'permissions' => $orgData['permissions']]
);
}
Auth::login($user);
return redirect('/dashboard');
});
// Express callback route
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
// Exchange code for token
const tokenRes = await fetch('https://id.kiges.cc/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: process.env.KIGES_CLIENT_ID,
client_secret: process.env.KIGES_CLIENT_SECRET,
redirect_uri: process.env.KIGES_REDIRECT_URI,
code,
}),
});
const tokens = await tokenRes.json();
// Fetch user profile + organizations
const meRes = await fetch('https://id.kiges.cc/api/v1/me', {
headers: { Authorization: `Bearer ${tokens.access_token}` },
});
const { data: userData } = await meRes.json();
// Create session with user data + organizations
req.session.user = {
uuid: userData.uuid,
name: userData.name,
email: userData.email,
organizations: userData.organizations,
accessToken: tokens.access_token,
};
res.redirect('/dashboard');
});
Organization Switching in Your SaaS
After authentication, the user may belong to multiple organizations. Here's how to implement organization switching in your SaaS.
function OrganizationSwitcher({ organizations, currentOrg, onSwitch }) {
return (
<select
value={currentOrg?.uuid || ''}
onChange={(e) => onSwitch(e.target.value)}
>
{organizations.map(org => (
<option key={org.uuid} value={org.uuid}>
{org.name} ({org.role})
</option>
))}
</select>
);
}
// When organization changes, re-fetch data scoped to that org
async function switchOrganization(orgUuid) {
// Store selected org in session/state
setCurrentOrg(orgUuid);
// Fetch org details + members from Kiges ID API
const orgRes = await fetch(`/api/kiges/organizations/${orgUuid}`);
const { data } = await orgRes.json();
// Check the user's permissions to show/hide UI elements
const canManage = data.your_permissions.includes('manage_members');
setCanManageMembers(canManage);
}
// In your SaaS backend, proxy requests to Kiges ID API
Route::middleware('auth')->prefix('api/kiges')->group(function () {
Route::get('/organizations', function (Request $request) {
return Http::withToken($request->user()->kiges_token)
->get('https://id.kiges.cc/api/v1/organizations')
->json();
});
Route::get('/organizations/{uuid}', function (Request $request, $uuid) {
return Http::withToken($request->user()->kiges_token)
->get("https://id.kiges.cc/api/v1/organizations/{$uuid}")
->json();
});
Route::get('/organizations/{uuid}/members', function (Request $request, $uuid) {
return Http::withToken($request->user()->kiges_token)
->get("https://id.kiges.cc/api/v1/organizations/{$uuid}/members")
->json();
});
Route::get('/organizations/{uuid}/permissions', function (Request $request, $uuid) {
$perms = $request->query('permissions', '');
return Http::withToken($request->user()->kiges_token)
->get("https://id.kiges.cc/api/v1/organizations/{$uuid}/permissions", [
'permissions' => $perms,
])
->json();
});
});
Webhooks
Overview
Kiges ID sends real-time HTTP POST notifications (webhooks) to your application whenever important events occur. Webhooks are delivered asynchronously with automatic retries on failure.
Automatic Setup
Configure your webhook URL when creating an OAuth client. A signing secret is auto-generated.
Signed Payloads
Every delivery is signed with HMAC-SHA256 so you can verify authenticity.
Automatic Retries
Failed deliveries retry up to 5 times with exponential backoff (30s, 2m, 10m, 30m, 1h).
Delivery Logs
Full delivery history with status, response, and manual retry via API.
Supported Events
| Event | Triggered When |
|---|---|
user.updated | A user updates their profile (name, email, etc.) |
organization.created | A new organization is created |
organization.updated | Organization name or description changes |
organization.deleted | An organization is deleted |
member.added | A user joins an organization (direct add, invitation, or invite link) |
member.updated | A member's role is changed |
member.removed | A member is removed from an organization |
role.created | A new custom role is created |
role.updated | A role's permissions or display name changes |
role.deleted | A custom role is deleted |
invitation.created | An invitation is sent |
invitation.accepted | An invitation is accepted |
invitation.declined | An invitation is declined |
invitation.cancelled | An invitation is cancelled by an admin |
GET /api/v1/webhook-events (authenticated).
Delivery Headers
Each webhook request includes these custom headers:
| Header | Description |
|---|---|
X-Kiges-Event | The event type (e.g., member.added) |
X-Kiges-Delivery | Unique delivery UUID (for idempotency) |
X-Kiges-Signature-256 | HMAC-SHA256 signature: sha256=<hex> |
X-Kiges-Timestamp | Unix timestamp of the delivery attempt |
User-Agent | KigesID-Webhook/1.0 |
Content-Type | application/json |
Signature Verification
Every webhook delivery is signed with your client's webhook secret using HMAC-SHA256. Always verify the signature before processing the payload.
The signature is computed as: HMAC-SHA256(timestamp + "." + raw_body, webhook_secret)
// In your SaaS webhook endpoint
Route::post('/webhooks/kiges', function (Request $request) {
$secret = config('services.kiges.webhook_secret');
$timestamp = $request->header('X-Kiges-Timestamp');
$signature = $request->header('X-Kiges-Signature-256');
$body = $request->getContent();
// Verify signature
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);
if (!hash_equals($expected, $signature)) {
return response('Invalid signature', 403);
}
// Optional: reject old timestamps (prevent replay attacks)
if (abs(time() - (int) $timestamp) > 300) {
return response('Timestamp too old', 403);
}
$event = $request->header('X-Kiges-Event');
$deliveryId = $request->header('X-Kiges-Delivery');
$payload = $request->json();
// Process the event
match ($event) {
'member.added' => $this->handleMemberAdded($payload),
'member.removed' => $this->handleMemberRemoved($payload),
'member.updated' => $this->handleMemberUpdated($payload),
'organization.updated' => $this->handleOrgUpdated($payload),
'role.updated' => $this->handleRoleUpdated($payload),
default => null, // Ignore unknown events
};
return response('OK', 200);
});
const crypto = require('crypto');
app.post('/webhooks/kiges', express.raw({ type: 'application/json' }), (req, res) => {
const secret = process.env.KIGES_WEBHOOK_SECRET;
const timestamp = req.headers['x-kiges-timestamp'];
const signature = req.headers['x-kiges-signature-256'];
const body = req.body.toString();
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(timestamp + '.' + body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
return res.status(403).send('Invalid signature');
}
const event = req.headers['x-kiges-event'];
const payload = JSON.parse(body);
switch (event) {
case 'member.added':
handleMemberAdded(payload);
break;
case 'member.removed':
handleMemberRemoved(payload);
break;
case 'organization.updated':
handleOrgUpdated(payload);
break;
// ... handle other events
}
res.status(200).send('OK');
});
import hmac
import hashlib
import time
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = 'your_webhook_secret'
@app.route('/webhooks/kiges', methods=['POST'])
def handle_webhook():
timestamp = request.headers.get('X-Kiges-Timestamp', '')
signature = request.headers.get('X-Kiges-Signature-256', '')
body = request.get_data(as_text=True)
expected = 'sha256=' + hmac.new(
WEBHOOK_SECRET.encode(),
f'{timestamp}.{body}'.encode(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
abort(403, 'Invalid signature')
if abs(time.time() - int(timestamp)) > 300:
abort(403, 'Timestamp too old')
event = request.headers.get('X-Kiges-Event')
payload = request.get_json()
if event == 'member.added':
handle_member_added(payload)
elif event == 'member.removed':
handle_member_removed(payload)
elif event == 'organization.updated':
handle_org_updated(payload)
return 'OK', 200
Payload Examples
member.added
{
"event": "member.added",
"timestamp": "2026-03-19T14:30:00+00:00",
"organization": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Corp",
"slug": "acme-corp"
},
"member": {
"user_uuid": "user-uuid-123",
"email": "john@example.com",
"name": "John",
"surname": "Doe",
"role": "member",
"permissions": ["view_members", "invite_members"]
},
"data": {
"role": "member",
"source": "invitation"
}
}
member.updated (role change)
{
"event": "member.updated",
"timestamp": "2026-03-19T14:35:00+00:00",
"organization": {
"uuid": "550e8400-...",
"name": "Acme Corp",
"slug": "acme-corp"
},
"member": {
"user_uuid": "user-uuid-123",
"email": "john@example.com",
"name": "John",
"surname": "Doe",
"role": "admin",
"permissions": ["manage_members", "manage_roles", ...]
},
"data": {
"old_role": "member",
"new_role": "admin"
}
}
organization.updated
{
"event": "organization.updated",
"timestamp": "2026-03-19T15:00:00+00:00",
"organization": {
"uuid": "550e8400-...",
"name": "Acme Corporation",
"slug": "acme-corp",
"description": "Updated description",
"is_active": true
},
"data": {
"name": "Acme Corporation"
}
}
role.created
{
"event": "role.created",
"timestamp": "2026-03-19T15:10:00+00:00",
"organization": {
"uuid": "550e8400-...",
"name": "Acme Corp",
"slug": "acme-corp"
},
"role": {
"id": 5,
"name": "project-manager",
"display_name": "Project Manager",
"permissions": ["manage_members", "invite_members", "view_members"]
}
}
invitation.accepted
{
"event": "invitation.accepted",
"timestamp": "2026-03-19T16:00:00+00:00",
"organization": {
"uuid": "550e8400-...",
"name": "Acme Corp",
"slug": "acme-corp"
},
"invitation": {
"uuid": "inv-uuid-123",
"email": "jane@example.com",
"role": "member"
}
}
List Webhook Deliveries
Query Parameters
| Param | Type | Description |
|---|---|---|
event | string | Filter by event type (e.g., member.added) |
status | string | Filter by status: pending, success, or failed |
per_page | integer | Items per page (max 100, default 25) |
Get Webhook Delivery
Returns full details of a webhook delivery, including the payload, response body, and error information.
{
"data": {
"uuid": "del-uuid-123",
"event": "member.added",
"url": "https://yourapp.com/webhooks/kiges",
"payload": { ... },
"status": "success",
"status_code": 200,
"response_body": "OK",
"error_message": null,
"attempt": 1,
"max_attempts": 5,
"next_retry_at": null,
"completed_at": "2026-03-19T14:30:01+00:00",
"created_at": "2026-03-19T14:30:00+00:00"
}
}
Retry Failed Delivery
Manually retry a failed webhook delivery. Resets the attempt counter and immediately queues a new delivery attempt.
{
"message": "Webhook delivery queued for retry",
"data": {
"uuid": "del-uuid-123",
"status": "pending"
}
}
Full Integration Examples
Python / Flask
import requests
KIGES_API = 'https://id.kiges.cc/api'
def get_user_with_orgs(access_token):
"""Fetch user profile and organizations in one call."""
r = requests.get(f'{KIGES_API}/v1/me', headers={
'Authorization': f'Bearer {access_token}',
'Accept': 'application/json',
})
r.raise_for_status()
return r.json()['data']
def get_org_members(access_token, org_uuid):
"""Fetch all members of an organization."""
r = requests.get(f'{KIGES_API}/v1/organizations/{org_uuid}/members', headers={
'Authorization': f'Bearer {access_token}',
'Accept': 'application/json',
})
r.raise_for_status()
return r.json()['data']
def check_user_permissions(access_token, org_uuid, permissions):
"""Check if the user has specific permissions in an organization."""
r = requests.get(
f'{KIGES_API}/v1/organizations/{org_uuid}/permissions',
params={'permissions': ','.join(permissions)},
headers={
'Authorization': f'Bearer {access_token}',
'Accept': 'application/json',
}
)
r.raise_for_status()
return r.json()['data']['checked']
# Usage
user = get_user_with_orgs(token)
for org in user['organizations']:
print(f"Org: {org['name']}, Role: {org['role']}")
perms = check_user_permissions(token, org['uuid'], ['manage_members', 'manage_billing'])
print(f" Can manage members: {perms['manage_members']}")
print(f" Can manage billing: {perms['manage_billing']}")
cURL Examples
# Get user profile + organizations
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://id.kiges.cc/api/v1/me
# List all organizations
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://id.kiges.cc/api/v1/organizations
# Get organization details
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://id.kiges.cc/api/v1/organizations/ORG_UUID
# List members
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://id.kiges.cc/api/v1/organizations/ORG_UUID/members
# Check specific permissions
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://id.kiges.cc/api/v1/organizations/ORG_UUID/permissions?permissions=manage_members,manage_billing"
# Invite a new member
curl -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email":"jane@example.com","role":"member"}' \
https://id.kiges.cc/api/v1/organizations/ORG_UUID/invitations
# Create a custom role
curl -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"editor","display_name":"Editor","permissions":["view_members","invite_members"]}' \
https://id.kiges.cc/api/v1/organizations/ORG_UUID/roles