Self-Hosted Onboarding

Set up AGLedger from scratch: create enterprise, agents, keys, and run your first mandate.

Production deployments: For repeatable, version-controlled setup, see YAML Provisioning — declare enterprises, agents, and schemas in YAML files instead of manual API calls. This guide covers the imperative API approach, which is better for exploration and one-off setup.

Prerequisites

Step 1: Create your enterprise

PLATFORM_KEY="ach_pla_your_platform_key"
API_BASE="https://agledger.your-company.com"

curl -X POST "$API_BASE/v1/admin/enterprises" \
  -H "Authorization: Bearer $PLATFORM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme Corp", "slug": "acme-corp"}'

Response:

{
  "id": "019d3b11-60c4-...",
  "name": "Acme Corp",
  "slug": "acme-corp",
  "trustLevel": "sandbox",
  "nextSteps": [
    {
      "action": "Upgrade trust level",
      "method": "PATCH",
      "href": "/v1/admin/accounts/019d3b11-60c4-.../trust-level",
      "description": "Upgrade from sandbox to active BEFORE creating API keys"
    },
    { "action": "Create an API key", "method": "POST", "href": "/v1/admin/api-keys" },
    { "action": "Create agents", "method": "POST", "href": "/v1/admin/agents" }
  ]
}

The API returns nextSteps guiding you through setup. New enterprises start at sandbox trust level — keys created at sandbox level can't access most endpoints.

ENTERPRISE_ID="019d3b11-60c4-..."

Step 2: Elevate trust level

Upgrade from sandbox to active before creating API keys:

curl -X PATCH "$API_BASE/v1/admin/accounts/$ENTERPRISE_ID/trust-level" \
  -H "Authorization: Bearer $PLATFORM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountType": "enterprise",
    "trustLevel": "active",
    "reason": "Initial setup"
  }'

Step 3: Create enterprise API key

curl -X POST "$API_BASE/v1/admin/api-keys" \
  -H "Authorization: Bearer $PLATFORM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ownerId": "'$ENTERPRISE_ID'",
    "ownerType": "enterprise",
    "role": "enterprise",
    "scopeProfile": "standard"
  }'

Response:

{ "apiKey": "ach_ent_abc123...", "id": "...", "role": "enterprise", "scopeProfile": "standard" }

Save this key — it's your enterprise's primary credential.

Important: Always specify scopeProfile (or explicit scopes) when creating keys. Without it, keys get restrictive defaults that may cause 403 errors on operations like schema registration. Use "standard" for enterprise keys and "agent-full" for agent keys to start with full access, then narrow later if needed.

Step 4: Create agents and keys

Create a principal agent (assigns work) and a performer agent (does work):

# Create principal agent
curl -X POST "$API_BASE/v1/admin/agents" \
  -H "Authorization: Bearer $PLATFORM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Procurement Orchestrator",
    "agentClass": "system",
    "ownerRef": "platform-team@acme.com",
    "orgUnit": "procurement",
    "description": "Orchestrates procurement mandates on behalf of the enterprise"
  }'
# → { "id": "019d3b11-6154-...", "trustLevel": "sandbox", "agentClass": "system" }
PRINCIPAL_ID="019d3b11-6154-..."

# Create performer agent
curl -X POST "$API_BASE/v1/admin/agents" \
  -H "Authorization: Bearer $PLATFORM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sourcing Agent",
    "agentClass": "system",
    "ownerRef": "platform-team@acme.com",
    "orgUnit": "procurement"
  }'
# → { "id": "019d3b11-6181-...", "agentClass": "system" }
PERFORMER_ID="019d3b11-6181-..."

Agent identity fields (all optional, all settable at creation or via PATCH /v1/agents/:id):

| Field | Description | Example | |-------|-------------|---------| | agentClass | personal, system, team, or ephemeral (default: system) | "system" | | ownerRef | Email or identity URI of the responsible person/team | "platform-team@acme.com" | | orgUnit | Department, team, or cost center | "procurement" | | description | Plain-text description of what this agent does (max 2000 chars) | "Orchestrates procurement mandates" |

These fields appear in audit trails and /auth/me responses, making it easier to trace which team owns which agent in production.

Elevate both to active and create their API keys:

# Elevate agents
for AGENT_ID in $PRINCIPAL_ID $PERFORMER_ID; do
  curl -X PATCH "$API_BASE/v1/admin/accounts/$AGENT_ID/trust-level" \
    -H "Authorization: Bearer $PLATFORM_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "accountType": "agent", "trustLevel": "active", "reason": "Initial setup" }'
done

# Create agent API keys
curl -X POST "$API_BASE/v1/admin/api-keys" \
  -H "Authorization: Bearer $PLATFORM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ownerId": "'$PRINCIPAL_ID'",
    "ownerType": "agent",
    "role": "agent",
    "scopeProfile": "agent-full"
  }'
# → { "apiKey": "ach_age_principal_...", "scopeProfile": "agent-full" }

curl -X POST "$API_BASE/v1/admin/api-keys" \
  -H "Authorization: Bearer $PLATFORM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ownerId": "'$PERFORMER_ID'",
    "ownerType": "agent",
    "role": "agent",
    "scopeProfile": "agent-full"
  }'
# → { "apiKey": "ach_age_performer_...", "scopeProfile": "agent-full" }

Scope profiles for agent keys: agent-full grants 8 scopes including mandates, receipts, disputes, and events. For agents that should only read data, use agent-readonly. Discover all profiles via GET /v1/admin/scope-profiles. See YAML Provisioning for the full profile table.

Approve agents for the enterprise:

curl -X PUT "$API_BASE/v1/enterprises/$ENTERPRISE_ID/agents/$PRINCIPAL_ID" \
  -H "Authorization: Bearer $PLATFORM_KEY" \
  -H "Content-Type: application/json" -d '{}'

curl -X PUT "$API_BASE/v1/enterprises/$ENTERPRISE_ID/agents/$PERFORMER_ID" \
  -H "Authorization: Bearer $PLATFORM_KEY" \
  -H "Content-Type: application/json" -d '{}'

Step 4b: Configure enforcement (optional)

Set enterprise-wide verification behavior:

curl -X PATCH "$API_BASE/v1/admin/enterprises/$ENTERPRISE_ID/config" \
  -H "Authorization: Bearer $PLATFORM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enforcement": {
      "toleranceEnforcement": "enforced",
      "deadlineEnforcement": "advisory",
      "schemaValidation": "advisory"
    }
  }'

Enforcement modes:

Step 5: Your first mandate

Now use the agent keys to run a real workflow:

PRINCIPAL_KEY="ach_age_principal_..."
PERFORMER_KEY="ach_age_performer_..."

# Principal creates mandate
curl -X POST "$API_BASE/v1/mandates/agent" \
  -H "Authorization: Bearer $PRINCIPAL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "principalAgentId": "'$PRINCIPAL_ID'",
    "performerAgentId": "'$PERFORMER_ID'",
    "contractType": "ACH-PROC-v1",
    "contractVersion": "1",
    "platform": "first-mandate",
    "criteria": {
      "item_description": "Office supplies for new team",
      "quantity": { "target": 10, "tolerance_pct": 10, "unit": "units" },
      "price_ceiling": { "amount": 500, "currency": "USD" }
    },
    "tolerance": { "quantityPct": 10, "priceMargin": 50, "graceSeconds": 3600 }
  }'
# → { "id": "019d3b11-aa52-...", "status": "PROPOSED" }
MANDATE_ID="019d3b11-aa52-..."

# Performer accepts
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/accept" \
  -H "Authorization: Bearer $PERFORMER_KEY" \
  -H "Content-Type: application/json" -d '{}'

# Principal activates
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/transition" \
  -H "Authorization: Bearer $PRINCIPAL_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "action": "activate" }'

# Performer submits receipt
curl -X POST "$API_BASE/v1/mandates/$MANDATE_ID/receipts" \
  -H "Authorization: Bearer $PERFORMER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agentId": "'$PERFORMER_ID'",
    "evidence": {
      "item_description": "Office supplies",
      "quantity": 10,
      "total_cost": { "amount": 450, "currency": "USD" },
      "supplier": { "id": "SUP-001", "name": "Office Depot" },
      "confirmation_ref": "PO-FIRST-001"
    }
  }'

# Check status (should be FULFILLED within seconds)
curl "$API_BASE/v1/mandates/$MANDATE_ID" \
  -H "Authorization: Bearer $PRINCIPAL_KEY"
# → { "status": "FULFILLED", ... }

Step 6: Verify it worked

ENTERPRISE_KEY="ach_ent_abc123..."

# Audit trail — every state transition recorded
curl "$API_BASE/v1/mandates/$MANDATE_ID/audit" \
  -H "Authorization: Bearer $ENTERPRISE_KEY"

# Identity check — who is this key?
curl "$API_BASE/v1/auth/me" \
  -H "Authorization: Bearer $ENTERPRISE_KEY"
# → { "ownerId": "019d3b11-60c4-...", "role": "enterprise", "trustLevel": "active" }

curl "$API_BASE/v1/auth/me" \
  -H "Authorization: Bearer $PRINCIPAL_KEY"
# → { "ownerId": "019d3b11-6154-...", "role": "agent", "trustLevel": "active" }

The setup sequence

1. POST /admin/enterprises            → create enterprise (sandbox)
2. PATCH /admin/accounts/:id/trust    → elevate to active
3. POST /admin/api-keys               → enterprise key (scopeProfile: standard)
4. POST /admin/agents                 → create agents (with identity fields)
5. PATCH /admin/accounts/:id/trust    → elevate agents to active
6. POST /admin/api-keys               → agent keys (scopeProfile: agent-full)
7. PUT /enterprises/:id/agents/:id    → approve agents
8. PATCH /admin/enterprises/:id/config → set enforcement (optional)
9. POST /mandates/agent               → first mandate

Total: 8-9 API calls from zero to first mandate.

Next: Onboard your agents

Your enterprise is set up and your first mandate works. If your AI agents will create mandates or submit receipts, pre-built agent skill instructions can teach agents the right contract types and field formats — reducing token cost by 17-47% and eliminating schema discovery errors.

Related guides


Validated end-to-end against API v0.19.17 on 2026-04-20: