API Reference

The Knowledge API is a REST API for managing decisions, invariants, rules, and the compliance workflow. All endpoints require authentication via Bearer token.

Base URL: https://api.asplenz.com/api/v1


Authentication

All requests must include an API key:

Authorization: Bearer kn_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

API keys are created via the /api-keys endpoint or during database seeding. Keys control:

  • Permissions: what operations are allowed
  • Scope access: which scopes the key can read/write
  • Tenant isolation: keys belong to one tenant

Core Entities

Scopes

MethodEndpointDescription
GET/scopesList scopes for the authenticated tenant
POST/scopesCreate a scope
GET/scopes/{scope_id}Get scope details with entry counts
PATCH/scopes/{scope_id}Update scope

Decisions

MethodEndpointDescription
GET/scopes/{scope_id}/decisionsList decisions
POST/scopes/{scope_id}/decisionsCreate a decision
GET/scopes/{scope_id}/decisions/{id}Get decision details

Create request:

{
  "decision": "Use PostgreSQL for transactional data",
  "context": "Evaluated PostgreSQL, DynamoDB, CockroachDB",
  "reasoning": "ACID guarantees without distributed complexity",
  "author": "sarah.chen",
  "author_type": "human",
  "tags": ["database", "infrastructure"],
  "namespace": "payments"
}

Invariants

MethodEndpointDescription
GET/scopes/{scope_id}/invariantsList invariants (active by default)
POST/scopes/{scope_id}/invariantsCreate an invariant
GET/scopes/{scope_id}/invariants/{id}Get invariant details
DELETE/scopes/{scope_id}/invariants/{id}Revoke (soft-delete)

Create request:

{
  "constraint": "All public endpoints require authentication",
  "rationale": "Security baseline",
  "requires_approval": true,
  "author": "security-team",
  "author_type": "human"
}

Rules

MethodEndpointDescription
GET/scopes/{scope_id}/rulesList rules (active by default)
POST/scopes/{scope_id}/rulesCreate a rule
GET/scopes/{scope_id}/rules/{id}Get rule with current version
PUT/scopes/{scope_id}/rules/{id}Update (creates new version)
GET/scopes/{scope_id}/rules/{id}/versionsList all versions
PATCH/scopes/{scope_id}/rules/{id}/archiveArchive the rule

Create request:

{
  "directive": "All PRs must be reviewed before merging",
  "severity": "MANDATORY",
  "author": "tech-lead",
  "author_type": "human"
}

Overrides

MethodEndpointDescription
GET/scopes/{scope_id}/overridesList overrides
POST/scopes/{scope_id}/overridesCreate an override
DELETE/scopes/{scope_id}/overrides/{id}Revoke an override

Create request:

{
  "target_id": "inv-8a3f1b2c4d5e",
  "override_type": "temporary",
  "justification": "Health endpoint must be public for load balancer",
  "conditions": "Only /health and /ready endpoints",
  "approved_by": "tech-lead",
  "expires_at": "2025-06-01T00:00:00Z"
}

Search and Compliance

Search

POST /search
{
  "query": "PostgreSQL",
  "scope_id": "scp-XXXX",
  "entry_type": "decision",
  "limit": 10
}

Searches across decision, context, reasoning (decisions), constraint, rationale (invariants), and directive (rules).

Compliance Check

POST /check
{
  "scope_id": "scp-XXXX",
  "intended_action": "Deploy on Friday evening without review"
}

Returns:

{
  "conflicts": [
    {
      "entry_id": "inv-8a3f1b2c4d5e",
      "entry_type": "invariant",
      "severity": "blocking",
      "requires_approval": true
    }
  ],
  "overridden": []
}

Resolve Normative State

GET /scopes/{scope_id}/resolve

Returns the complete normative state: all active invariants, rules, and overrides, plus a normative_hash for change detection.


References and Events

References

MethodEndpointDescription
GET/scopes/{scope_id}/referencesList references
POST/scopes/{scope_id}/referencesRecord a reference
GET/references?entry_id=xxxGet references for an entry
GET/references?context_ref=xxxGet references for a context

Create request:

{
  "entry_id": "inv-8a3f1b2c4d5e",
  "context_type": "pull_request",
  "context_ref": "github:acme/api/pull_request#42",
  "status": "followed",
  "author": "claude-agent",
  "author_type": "agent"
}

Status values: cited, followed, verified, diverged (requires reason).

Events

MethodEndpointDescription
GET/scopes/{scope_id}/eventsList events
GET/scopes/{scope_id}/events?type=xxxFilter by event type
GET/scopes/{scope_id}/events?after=xxxCatch-up from event ID

Approval Workflow

MethodEndpointDescription
POST/scopes/{scope_id}/approvalsRequest approval
GET/scopes/{scope_id}/approvalsList approvals
GET/scopes/{scope_id}/approvals/{id}Get approval details
POST/scopes/{scope_id}/approvals/{id}/decideApprove or reject

Request approval:

{
  "trigger_id": "inv-8a3f1b2c4d5e",
  "trigger_type": "invariant",
  "intended_action": "Add public health endpoint",
  "justification": "Load balancer needs unauthenticated health check",
  "requested_by": "deploy-bot",
  "requested_by_type": "agent"
}

Decide:

{
  "decision": "approved",
  "comment": "Approved for health/ready endpoints only",
  "decided_by": "tech-lead"
}

If approved for an invariant/rule with requires_approval, an override is auto-created.

Webhook notifications

Instead of polling GET /approvals/{id}, subscribers can receive ECDSA-signed webhook POSTs when approvals are requested or resolved.

MethodEndpointDescription
GET/.well-known/webhook-keyPublic key for signature verification (no auth)
POST/scopes/{scope_id}/webhooksCreate a subscription (admin)
GET/scopes/{scope_id}/webhooksList subscriptions
GET/webhooks/{subscription_id}Get subscription details
PATCH/webhooks/{subscription_id}Update a subscription (admin)
DELETE/webhooks/{subscription_id}Delete a subscription (admin)

Create a subscription:

{
  "url": "https://your-service.example.com/webhook",
  "events": ["approval.requested", "approval.resolved"],
  "description": "Notify Slack bot on approvals"
}

Each webhook POST includes headers for verification:

  • X-Knowledge-Event — event type
  • X-Knowledge-Timestamp — Unix timestamp
  • X-Knowledge-Signature — hex-encoded ECDSA P-256/SHA-256 signature

Verify by reconstructing the message ({timestamp}.{raw_body}) and checking against the public key from /.well-known/webhook-key.


Links

MethodEndpointDescription
POST/linksCreate a link between entries
GET/links?entity_id=xxxGet links for an entity

Create link:

{
  "from_id": "dec-abc123",
  "from_type": "decision",
  "to_id": "inv-def456",
  "to_type": "invariant",
  "relation": "creates"
}

Relation types: depends_on, supersedes, contradicts, in_tension_with, creates.


Tenants and Namespaces

Tenants

MethodEndpointDescription
GET/tenantsList tenants
POST/tenantsCreate a tenant

Namespaces

MethodEndpointDescription
GET/scopes/{scope_id}/namespacesList namespaces
POST/scopes/{scope_id}/namespacesCreate a namespace

Namespaces support hierarchy: paymentspayments/stripe → max depth 2 levels below root.


Error Responses

{
  "detail": "Not authorized: missing permission manage_rules"
}
StatusMeaning
400Bad request (validation error)
401Missing or invalid API key
403Insufficient permissions
404Resource not found
422Validation error (missing required field)

Interactive Documentation

When the API is running, visit https://api.asplenz.com/docs for the auto-generated Swagger UI with try-it-out capability.