Darwan

Developer Documentation

Darwan Authorization API

Integrate role-based access control, attribute policies, and compliance workflows into any service. One API for authorization, audit, and regulatory readiness.

RBACABACMulti-TenantSOX / SOC 2ISO 27001HIPAA

Concepts

Tenant

Isolation boundary. Every request includes tenantId.

Principal

User or service identity. Every request includes principalId.

Permission Key

Format: domain.resourceType.action (e.g. erp.invoice.read).

RBAC + ABAC

Roles grant permissions; ABAC rules refine; deny overrides allow.

Time-Bound

Role grants can expire automatically via expiresAt.

SoD Constraints

Conflicting roles are blocked or warned at assignment time.

Access Reviews

Periodic certification campaigns to approve or revoke assignments.

Audit Immutability

Every decision is SHA-256 hash-chained with per-tenant sequencing.

Authentication

All API requests require a Darwan API key passed as a Bearer token. OAuth is only used for the admin UI login — not for API calls.

API Key Format

Authorization Header
Authorization: Bearer dk_live_a1b2c3d4.e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4
Content-Type: application/json

dk_ — fixed prefix

live or test — environment

a1b2c3d4 — 8-character key ID

.e5f6... — 32-character secret (after the dot)

Create an API Key

Create API keys via the admin UI at darwan.net/dashboard or via the API:

Create API Key
curl -X POST https://darwan.net/v1/api-keys \
  -H "Authorization: Bearer dk_live_YOUR_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "YOUR_TENANT_ID",
    "name": "My Service Key",
    "environment": "live",
    "scopes": ["authorize", "roles:write", "permissions:write", "assignments:write"]
  }'

Quick Smoke Test

Verify your key works
curl -H "Authorization: Bearer dk_live_YOUR_KEY" https://darwan.net/v1/tenants

# 200 = key works with admin scope
# 403 = key works but lacks admin scope
# 401 = key is invalid, revoked, or expired
Security: API keys are shown only once at creation time. Store them securely. Use rotation (POST /v1/api-keys/{id}/rotate) to replace keys without downtime.

Quick Start

01

Create Tenant & Roles

Use the admin console or API to create a tenant, define roles, and assign permissions.

02

Assign Roles

Assign roles to principals or groups. Optionally set expiry dates and SoD constraints.

03

Authorize

Call POST /v1/authorize from your service. Get an allow/deny decision with audit trail.

Full Working Example

Given a tenant ID and API key, here is the exact sequence to set up roles and permissions:

Step 1: Create a Role
curl -X POST https://darwan.net/v1/roles \
  -H "Authorization: Bearer dk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "YOUR_TENANT_ID",
    "name": "editor",
    "description": "Can read and write articles"
  }'
Step 2: Create a Permission
curl -X POST https://darwan.net/v1/permissions \
  -H "Authorization: Bearer dk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "YOUR_TENANT_ID",
    "name": "article.write",
    "description": "Write articles",
    "resourceType": "article",
    "action": "write"
  }'
Step 3: Assign Permission to Role
curl -X POST https://darwan.net/v1/assignments/role-permission \
  -H "Authorization: Bearer dk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "YOUR_TENANT_ID",
    "roleId": "ROLE_ID_FROM_STEP_1",
    "permissionId": "PERMISSION_ID_FROM_STEP_2"
  }'
Step 4: Create a Principal
curl -X POST https://darwan.net/v1/principals \
  -H "Authorization: Bearer dk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "YOUR_TENANT_ID",
    "externalId": "user-123",
    "displayName": "John Doe"
  }'
Step 5: Assign Role to Principal
curl -X POST https://darwan.net/v1/assignments/principal-role \
  -H "Authorization: Bearer dk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "YOUR_TENANT_ID",
    "principalId": "PRINCIPAL_ID_FROM_STEP_4",
    "roleId": "ROLE_ID_FROM_STEP_1"
  }'
Step 6: Authorize
curl -X POST https://darwan.net/v1/authorize \
  -H "Authorization: Bearer dk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "YOUR_TENANT_ID",
    "principalId": "PRINCIPAL_ID_FROM_STEP_4",
    "action": "write",
    "resourceType": "article"
  }'

# Response: { "decision": "allow" }
Security: Order matters: tenant must exist first, then roles and permissions, then assign permissions to roles, then create principals, then assign roles to principals. Only then can you authorize.

Tenant Management

Tenants are the top-level isolation boundary. Every resource (roles, permissions, assignments, API keys) belongs to a tenant. Use the admin console or API to create and manage tenants.

Create a Tenant

POST /v1/tenants
{
  "name": "Acme Corp",
  "planTier": "pro"
}

Returns the created tenant with its generated id. You can optionally pass an id field to use a specific UUID.

List Tenants

GET /v1/tenants
[
  {
    "id": "00000000-0000-0000-0000-000000000001",
    "name": "Acme Corp",
    "planTier": "pro",
    "status": "Active"
  }
]

Deactivate a Tenant

DELETE /v1/tenants/{id}
{
  "id": "00000000-0000-0000-0000-000000000001",
  "name": "Acme Corp",
  "planTier": "pro",
  "status": "Suspended"
}

Soft-deletes the tenant by setting status to Suspended. Associated resources become inaccessible but are not deleted.

FieldTypeRequiredDescription
namestringYesUnique tenant name
planTierstringNoPlan tier: free, starter, pro, enterprise. Defaults to free.
idGuidNoOptional custom UUID. Auto-generated if omitted.
Note: All tenant endpoints require the admin scope. Use an API key with admin scope or sign in via the console.

Authorization Request

Call POST /v1/authorize with the principal, action, and resource. Darwan evaluates RBAC grants, ABAC policies, time-bound assignments, and returns a decision.

Request Body
{
  "tenantId": "00000000-0000-0000-0000-000000000001",
  "principalId": "00000000-0000-0000-0000-000000000010",
  "action": "read",
  "resourceType": "invoice",
  "resourceId": "inv-001",
  "subject": { "team": "finance" },
  "resource": { "owner_id": "user-1" },
  "context": { "ip": "127.0.0.1" }
}
Response (Allowed)
{
  "allowed": true,
  "decision": "allow",
  "reason": "RBAC grant matched"
}

Explainable Checks

Call POST /v1/authorize:explain to get full decision metadata for debugging and compliance evidence.

Response
{
  "allowed": true,
  "decision": "allow",
  "decisionId": "d-001",
  "policyVersion": 3,
  "reasons": ["Principal has role 'finance-viewer'"],
  "matchedRules": [
    { "ruleId": "r-1", "effect": "allow", "source": "rbac" }
  ]
}

Batch Checks

Use POST /v1/authorize/batch to evaluate multiple authorization checks in a single request, reducing network round-trips.

Request Body
{
  "items": [
    {
      "requestId": "req-1",
      "tenantId": "...",
      "principalId": "...",
      "action": "read",
      "resourceType": "invoice"
    },
    {
      "requestId": "req-2",
      "tenantId": "...",
      "principalId": "...",
      "action": "write",
      "resourceType": "invoice"
    }
  ]
}

Caching Guidance

Cache only RBAC-only decisions (no attributes, no policies).

Keep TTL short (60-120 seconds).

Invalidate on webhook events.

Important: Do not cache decisions that involve ABAC attributes or time-sensitive context. These can change between requests.

Role Assignments

Assign roles to principals directly or through groups.

POST /v1/assignments/principal-role
{
  "tenantId": "...",
  "principalId": "...",
  "roleId": "...",
  "expiresAt": "2026-06-01T00:00:00Z"
}
POST /v1/assignments/group-role
{
  "tenantId": "...",
  "groupId": "...",
  "roleId": "...",
  "expiresAt": null
}
FieldTypeRequiredDescription
tenantIdGuidYesTenant scope
principalId / groupIdGuidYesTarget principal or group
roleIdGuidYesRole to assign
expiresAtDateTimeOffset?NoAuto-expire date. Past dates rejected (400).

Time-Bound Assignments

Role assignments can have an optional expiresAt field. Expired assignments are automatically excluded from authorization and cleaned up.

Auto-Filtered

Authorization checks apply: ExpiresAt == null || ExpiresAt > UtcNow

Background Cleanup

AssignmentExpiryService runs every 5 minutes to delete expired rows.

Past Date Rejected

Setting expiresAt to a past date returns 400 Bad Request.

Permanent by Default

Omit expiresAt or pass null for assignments that never expire.

Separation of Duties (SoD)

SoD constraints prevent conflicting roles from being assigned to the same principal. Required by SOX, PCI DSS, and NIST 800-53.

Create a Constraint

POST /v1/sod-constraints
{
  "tenantId": "...",
  "name": "Finance SoD",
  "description": "Approver and Requester cannot be the same person",
  "conflictingRoleIds": ["<role-a-id>", "<role-b-id>"],
  "enforcement": "block"
}
EnforcementBehavior
blockReturns 409 Conflict with violation details. Assignment is rejected.
warnAssignment proceeds. Warning included in response body.
Note: SoD validation checks both direct principal-role assignments and indirect group-role paths. Group-role assignments check all principals in the group.

Access Reviews

Periodic access reviews let you certify that all role assignments are still appropriate. Required by SOX, SOC 2, ISO 27001, and PCI DSS.

Create a Review

POST /v1/access-reviews
{
  "tenantId": "...",
  "name": "Q1 2026 Review",
  "reviewerPrincipalId": "...",
  "dueDate": "2026-03-31T23:59:59Z",
  "includeGroupRoles": true
}

Creates a review with status pending. Generates one item per current role assignment. Set includeGroupRoles: true to also review group-role assignments.

Decide Items

POST /v1/access-reviews/{id}/items/{itemId}/decide
{
  "decision": "approved",
  "reviewedByPrincipalId": "...",
  "notes": "Verified with manager"
}

approved — assignment is retained.

revoked — the underlying role assignment is deleted immediately.

Complete Review

Call POST /v1/access-reviews/{id}/complete. Returns 409 if pending items remain. Sets status to completed.

Auto-Expiry

Note: The AccessReviewReminderService runs every 6 hours. Overdue reviews (past dueDate and still pending or in-progress) are automatically set to expired.

Audit Events

Every authorization decision is logged as an audit event. Query, summarize, and export audit data for compliance reporting.

Query

GET /v1/audit-events
GET /v1/audit-events?tenantId=...&page=1&pageSize=50

Optional filters:
  principalId, action, resourceType,
  decision (allow/deny),
  startDate, endDate

Returns paginated response: items, page, pageSize, totalCount, totalPages.

Summary

GET /v1/audit-events/summary
GET /v1/audit-events/summary?tenantId=...&period=30d

Response:
{
  "total": 12450,
  "allowCount": 11200,
  "denyCount": 1250,
  "period": "30d"
}

Periods: 7d, 30d, 90d.

Export

GET /v1/audit-events/export?tenantId=...&format=csv

csv — returns Content-Type: text/csv with header row.

json — returns an array of audit event objects.

Maximum 10,000 events per export. Same filters as query.

Audit Integrity

Every audit event is assigned a per-tenant sequence number and a SHA-256 hash that chains to the previous event. This makes tampering detectable.

GET /v1/audit-events/verify
GET /v1/audit-events/verify?tenantId=...&startSequence=1&limit=1000

Response:
{
  "verified": true,
  "checkedCount": 1000,
  "firstInvalidSequence": null,
  "message": "Chain intact"
}

verified: true

The hash chain is intact. No tampering detected.

verified: false

Returns the sequence number of the first tampered event.

Security: Old audit events are archived (copied to AuditArchives) rather than deleted. The hash chain remains verifiable through the archive.

API Key Scopes

When creating API keys, scope access to specific capabilities:

ScopeAccess
authorizeCall authorization endpoints
assignments:read / :writeView and manage role assignments
audit:read / audit:exportQuery and export audit events
compliance:read / :writeManage SoD constraints and access reviews
adminFull access to all endpoints

Webhooks

Subscribe to events to sync caches and downstream systems in real-time.

EventTrigger
role.createdNew role created
permission.createdNew permission created
assignment.principal_role.createdRole assigned to principal
assignment.role_permission.createdPermission linked to role
sod_constraint.createdNew SoD constraint created
access_review.createdNew access review campaign started
access_review.item_decidedReview item approved or revoked
access_review.completedAll items decided, review finalized

Endpoint Reference

Complete list of API endpoints grouped by domain.

Tenants

MethodEndpointDescriptionScope
POST/v1/tenantsCreate tenantadmin
GET/v1/tenantsList all tenantsadmin
DELETE/v1/tenants/{id}Deactivate tenantadmin

Authorization

MethodEndpointDescriptionScope
POST/v1/authorizeEvaluate authorizationauthorize
POST/v1/authorize:explainExplainable decisionauthorize
POST/v1/authorize/batchBatch authorizationauthorize

Assignments

MethodEndpointDescriptionScope
POST/v1/assignments/principal-roleAssign role to principalassignments:write
GET/v1/assignments/principal-roleList principal-role assignmentsassignments:read
POST/v1/assignments/group-roleAssign role to groupassignments:write
GET/v1/assignments/group-roleList group-role assignmentsassignments:read

Compliance

MethodEndpointDescriptionScope
POST/v1/sod-constraintsCreate SoD constraintcompliance:write
GET/v1/sod-constraintsList SoD constraintscompliance:read
DELETE/v1/sod-constraints/{id}Delete SoD constraintcompliance:write
POST/v1/access-reviewsCreate access reviewcompliance:write
GET/v1/access-reviewsList access reviewscompliance:read
GET/v1/access-reviews/{id}Get review with itemscompliance:read
POST/v1/access-reviews/{id}/items/{itemId}/decideApprove or revoke itemcompliance:write
POST/v1/access-reviews/{id}/completeComplete reviewcompliance:write

Audit

MethodEndpointDescriptionScope
GET/v1/audit-eventsPaginated audit log queryaudit:read
GET/v1/audit-events/summaryEvent count summaryaudit:read
GET/v1/audit-events/exportCSV/JSON exportaudit:export
GET/v1/audit-events/verifyHash chain verificationaudit:read

SDK Quick Starts

Official SDKs for your stack. Authorize with a single function call.

C#
var client = new Darwan.Sdk.DarwanClient(
    new HttpClient { BaseAddress = new Uri("https://darwan.net") });

var decision = await client.AuthorizeAsync(new AuthorizeRequest
{
    TenantId = Guid.Parse("..."),
    PrincipalId = Guid.Parse("..."),
    Action = "read",
    ResourceType = "invoice"
});

if (decision.Allowed)
    Console.WriteLine("Access granted");

Regulatory Readiness

Built for compliance from the ground up

SOX

  • SoD constraints
  • Access reviews
  • Immutable audit logs

SOC 2

  • Audit trails
  • Access reviews
  • Time-bound access
  • Integrity verification

ISO 27001

  • RBAC
  • Periodic reviews
  • Audit logging
  • Least privilege

HIPAA

  • Access controls
  • Audit logs
  • Session expiry
  • Review certification

PCI DSS

  • Role-based access
  • SoD
  • Quarterly reviews
  • Audit retention

GDPR

  • Access control
  • Audit trails
  • Data minimization
  • Review evidence

NIST 800-53

  • RBAC/ABAC
  • SoD
  • Continuous monitoring
  • Audit integrity

Need Help?

Our team is here to help you integrate Darwan into your services. Check the console or reach out for support.

A KaritKarma Limited platform