AI Minority Lab learner API

Exercise version 1.0.0 pins this exact OpenAPI hash and rendered documentation artifact. Historical pins stay immutable even when the live contract later evolves.

Back to dashboard

Versioned artifact

Exercise version
1.0.0
OpenAPI hash
68af75e4ddbffcd3f626af0839b95a2025563c3ea1dbf3201cc841c045fbb7fa
Rendered docs hash
d3cc1d01a833cf37e96cd811fac0b6746885f94bde520fe3833e4c36596e7047
Base URL
https://api.aiminority.dev

Authentication

Authenticate every learner API call with Authorization: Bearer <api-token>.

Create and rotate the token from the authenticated lab dashboard.

The raw token is shown only at creation; store it outside the platform.

Missing or invalid credentials return 401 UNAUTHORIZED without revealing token state.

Resource relationships

GET /v1/simulations lists simulations you own with id, purpose, and lifecycle.

Create practice and assessment simulations from the authenticated lab dashboard.

Address one simulation explicitly under /v1/simulations/{simulationId}/…. There is no implicit active-simulation routing.

Tickets expose relationship IDs for customer and order; follow those IDs through the documented read endpoints.

Orders include their item array because refund allocations need item identifiers and refundable balances.

Money uses integer minor units. Project scenarios use USD cents. Opaque UUIDs identify every relationship.

No endpoint returns a prejoined case view, recommended outcome, or policy interpretation.

Errors

Non-success responses use RFC 9457 Problem Details with type, title, status, detail, instance, code, and correlation_id.

404 is returned for resources that are missing or not owned by the authenticated learner.

409 covers simulation lifecycle, ticket state, financial, and idempotency conflicts.

429 includes Retry-After. Unexpected failures return a generic 500 plus correlation ID.

Refund creation may return 504 CONTROLLED_GATEWAY_TIMEOUT after the refund has already committed; inspect state before retrying.

Limits

Ticket listing defaults to status=open and limit=50, with a maximum limit of 100.

Supported ticket filters are open, waiting_for_customer, escalated, resolved, and all.

Cursors are opaque, tamper-resistant, and bound to user, simulation, filter, and page position.

Practice reset and practice check endpoints are rate limited independently of assessment submission.

Idempotency

Refund creation accepts an optional Idempotency-Key header scoped to the simulation.

An identical same-key retry returns the original 201 body with Idempotency-Replayed: true.

Reusing a key with different input returns 409 IDEMPOTENCY_CONFLICT.

Without a key, every accepted request is a new refund attempt when invariants allow it.

Practice reset

POST /v1/simulations/{simulationId}/reset is available for practice simulations only.

A successful reset retires the current practice simulation and returns a replacement active practice simulation.

Assessment simulations reject reset with a lifecycle conflict.

Practice checks

POST /v1/simulations/{simulationId}/checks runs non-locking practice feedback against the current practice state.

Feedback is category-level with safe remediation codes; it does not expose hidden assessment expectations.

Assessment simulations reject practice checks.

Assessment submission

POST /v1/simulations/{simulationId}/submissions locks an assessment simulation and returns the immutable safe submission report.

Retrieve the same safe report later with GET /v1/simulations/{simulationId}/submissions/{submissionId}.

After submission, mutations are rejected while authenticated reads remain available.

The safe report never includes hidden expectations, seeds, failure triggers, or validator internals.

Documented requests

Every request below is exercised by an executable contract test against the pinned schemas.

GET /v1/simulations

List owned simulations with explicit IDs, purpose, and lifecycle.

Success status: 200

Path parameters

            {}
          

Example response

                {
  "items": [
    {
      "id": "11111111-1111-4111-8111-111111111111",
      "purpose": "practice",
      "lifecycle": "active"
    }
  ]
}
              

GET /v1/simulations/{simulationId}

Read the simulation lifecycle and current simulation time.

Success status: 200

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111"
}
          

Example response

                {
  "id": "11111111-1111-4111-8111-111111111111",
  "purpose": "practice",
  "lifecycle": "active",
  "exercise_version": "1.0.0",
  "scenario_version": "1.0.0",
  "simulation_time": "2026-09-15T12:00:00.000Z"
}
              

GET /v1/simulations/{simulationId}/tickets

List tickets with opaque cursor pagination.

Success status: 200

Query

                {
  "status": "open",
  "limit": "50"
}
              

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111"
}
          

Example response

                {
  "items": [
    {
      "id": "22222222-2222-4222-8222-222222222222",
      "customer_id": "33333333-3333-4333-8333-333333333333",
      "order_id": "44444444-4444-4444-8444-444444444444",
      "subject": "Shipment never arrived",
      "body": "I still have not received order ACM-10482.",
      "status": "open",
      "resolution": null,
      "priority": "normal",
      "created_at": "2026-09-15T12:00:00.000Z"
    }
  ],
  "next_cursor": null
}
              

GET /v1/simulations/{simulationId}/tickets/{ticketId}

Read one ticket and its relationship IDs.

Success status: 200

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111",
  "ticketId": "22222222-2222-4222-8222-222222222222"
}
          

Example response

                {
  "id": "22222222-2222-4222-8222-222222222222",
  "customer_id": "33333333-3333-4333-8333-333333333333",
  "order_id": "44444444-4444-4444-8444-444444444444",
  "subject": "Shipment never arrived",
  "body": "I still have not received order ACM-10482.",
  "status": "open",
  "resolution": null,
  "priority": "normal",
  "created_at": "2026-09-15T12:00:00.000Z"
}
              

GET /v1/simulations/{simulationId}/customers/{customerId}

Read the customer referenced by a ticket.

Success status: 200

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111",
  "customerId": "33333333-3333-4333-8333-333333333333"
}
          

Example response

                {
  "id": "33333333-3333-4333-8333-333333333333",
  "name": "Jordan Lee",
  "email": "jordan.lee@example.com",
  "risk_level": "standard",
  "created_at": "2026-08-15T10:00:00.000Z"
}
              

GET /v1/simulations/{simulationId}/orders/{orderId}

Read an order aggregate, including refundable items.

Success status: 200

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111",
  "orderId": "44444444-4444-4444-8444-444444444444"
}
          

Example response

                {
  "id": "44444444-4444-4444-8444-444444444444",
  "customer_id": "33333333-3333-4333-8333-333333333333",
  "order_number": "ACM-10482",
  "status": "fulfilled",
  "currency": "USD",
  "total_amount": 7999,
  "refundable_amount": 7999,
  "items": [
    {
      "id": "55555555-5555-4555-8555-555555555555",
      "sku": "ACME-MUG",
      "name": "Acme Mug",
      "quantity": 1,
      "paid_unit_amount": 7999,
      "refundable_amount": 7999,
      "refundability": "refundable"
    }
  ],
  "created_at": "2026-09-01T09:00:00.000Z"
}
              

GET /v1/simulations/{simulationId}/orders/{orderId}/shipments

List shipments for an order.

Success status: 200

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111",
  "orderId": "44444444-4444-4444-8444-444444444444"
}
          

Example response

                [
  {
    "id": "66666666-6666-4666-8666-666666666666",
    "order_id": "44444444-4444-4444-8444-444444444444",
    "carrier": "ExampleCarrier",
    "tracking_number": "EX-1001",
    "status": "lost_in_transit",
    "dispatched_at": "2026-09-02T08:00:00.000Z",
    "estimated_delivery_at": "2026-09-10T08:00:00.000Z",
    "status_updated_at": "2026-09-12T08:00:00.000Z",
    "delivered_at": null
  }
]
              

GET /v1/simulations/{simulationId}/orders/{orderId}/refunds

List refunds already created for an order.

Success status: 200

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111",
  "orderId": "44444444-4444-4444-8444-444444444444"
}
          

Example response

                [
  {
    "id": "77777777-7777-4777-8777-777777777777",
    "order_id": "44444444-4444-4444-8444-444444444444",
    "ticket_id": "22222222-2222-4222-8222-222222222222",
    "status": "completed",
    "reason": "lost_in_transit",
    "currency": "USD",
    "amount": 7999,
    "allocations": [
      {
        "id": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
        "order_item_id": "55555555-5555-4555-8555-555555555555",
        "quantity": 1,
        "amount": 7999
      }
    ],
    "created_at": "2026-09-15T12:00:00.000Z"
  }
]
              

POST /v1/simulations/{simulationId}/orders/{orderId}/refunds

Create a refund with optional idempotency.

Success status: 201

Headers

                
                  idempotency-key: learner-refund-1
                
              

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111",
  "orderId": "44444444-4444-4444-8444-444444444444"
}
          

Request body

                {
  "ticket_id": "22222222-2222-4222-8222-222222222222",
  "reason": "lost_in_transit",
  "items": [
    {
      "order_item_id": "55555555-5555-4555-8555-555555555555",
      "quantity": 1,
      "amount": 7999
    }
  ]
}
              

Example response

                {
  "refund": {
    "id": "77777777-7777-4777-8777-777777777777",
    "order_id": "44444444-4444-4444-8444-444444444444",
    "ticket_id": "22222222-2222-4222-8222-222222222222",
    "status": "completed",
    "reason": "lost_in_transit",
    "currency": "USD",
    "amount": 7999,
    "allocations": [
      {
        "id": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
        "order_item_id": "55555555-5555-4555-8555-555555555555",
        "quantity": 1,
        "amount": 7999
      }
    ],
    "created_at": "2026-09-15T12:00:00.000Z"
  },
  "order": {
    "id": "44444444-4444-4444-8444-444444444444",
    "customer_id": "33333333-3333-4333-8333-333333333333",
    "order_number": "ACM-10482",
    "status": "fulfilled",
    "currency": "USD",
    "total_amount": 7999,
    "refundable_amount": 0,
    "items": [
      {
        "id": "55555555-5555-4555-8555-555555555555",
        "sku": "ACME-MUG",
        "name": "Acme Mug",
        "quantity": 1,
        "paid_unit_amount": 7999,
        "refundable_amount": 7999,
        "refundability": "refundable"
      }
    ],
    "created_at": "2026-09-01T09:00:00.000Z"
  },
  "ticket": {
    "id": "22222222-2222-4222-8222-222222222222",
    "customer_id": "33333333-3333-4333-8333-333333333333",
    "order_id": "44444444-4444-4444-8444-444444444444",
    "subject": "Shipment never arrived",
    "body": "I still have not received order ACM-10482.",
    "status": "open",
    "resolution": null,
    "priority": "normal",
    "created_at": "2026-09-15T12:00:00.000Z"
  },
  "simulation_time": "2026-09-15T12:01:00.000Z"
}
              

GET /v1/simulations/{simulationId}/policies/refunds

Retrieve published refund policy text for the simulation.

Success status: 200

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111"
}
          

Example response

                [
  {
    "id": "88888888-8888-4888-8888-888888888888",
    "policy_type": "refund",
    "version": "1.0.0",
    "publication_state": "published",
    "content": "Inspect order, shipment, and existing refunds before acting. Use an idempotency key before retrying an ambiguous refund.",
    "effective_from": "2026-01-01T00:00:00.000Z",
    "effective_to": null
  }
]
              

GET /v1/simulations/{simulationId}/audit-events

Read sanitized learner-visible audit events.

Success status: 200

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111"
}
          

Example response

                {
  "items": [
    {
      "id": "99999999-9999-4999-8999-999999999999",
      "actor": "learner_api",
      "operation": "refund.create",
      "entity_type": "refund",
      "entity_id": "77777777-7777-4777-8777-777777777777",
      "simulation_time": "2026-09-15T12:00:00.000Z",
      "correlation_id": "corr_example_1",
      "idempotency_key_present": true,
      "accepted": true,
      "rejection_category": null
    }
  ],
  "next_cursor": null
}
              

POST /v1/simulations/{simulationId}/tickets/{ticketId}/request-information

Request information and move the ticket to waiting_for_customer.

Success status: 200

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111",
  "ticketId": "22222222-2222-4222-8222-222222222222"
}
          

Request body

                {
  "message": "Please share the order number for this request."
}
              

Example response

                {
  "ticket": {
    "id": "22222222-2222-4222-8222-222222222222",
    "customer_id": "33333333-3333-4333-8333-333333333333",
    "order_id": "44444444-4444-4444-8444-444444444444",
    "subject": "Shipment never arrived",
    "body": "I still have not received order ACM-10482.",
    "status": "waiting_for_customer",
    "resolution": null,
    "priority": "normal",
    "created_at": "2026-09-15T12:00:00.000Z"
  },
  "message_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
  "escalation_id": null,
  "simulation_time": "2026-09-15T12:01:00.000Z"
}
              

POST /v1/simulations/{simulationId}/tickets/{ticketId}/escalations

Escalate a ticket without issuing a refund.

Success status: 201

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111",
  "ticketId": "22222222-2222-4222-8222-222222222222"
}
          

Request body

                {
  "reason": "Conflicting evidence requires human review.",
  "message": "We are escalating this request for specialist review."
}
              

Example response

                {
  "ticket": {
    "id": "22222222-2222-4222-8222-222222222222",
    "customer_id": "33333333-3333-4333-8333-333333333333",
    "order_id": "44444444-4444-4444-8444-444444444444",
    "subject": "Shipment never arrived",
    "body": "I still have not received order ACM-10482.",
    "status": "escalated",
    "resolution": null,
    "priority": "normal",
    "created_at": "2026-09-15T12:00:00.000Z"
  },
  "message_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
  "escalation_id": "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
  "simulation_time": "2026-09-15T12:01:00.000Z"
}
              

POST /v1/simulations/{simulationId}/tickets/{ticketId}/close

Close a ticket with a structured resolution.

Success status: 200

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111",
  "ticketId": "22222222-2222-4222-8222-222222222222"
}
          

Request body

                {
  "resolution": "refund_completed",
  "message": "We confirmed that your shipment was lost and issued a full refund of $79.99 for order ACM-10482."
}
              

Example response

                {
  "ticket": {
    "id": "22222222-2222-4222-8222-222222222222",
    "customer_id": "33333333-3333-4333-8333-333333333333",
    "order_id": "44444444-4444-4444-8444-444444444444",
    "subject": "Shipment never arrived",
    "body": "I still have not received order ACM-10482.",
    "status": "resolved",
    "resolution": "refund_completed",
    "priority": "normal",
    "created_at": "2026-09-15T12:00:00.000Z"
  },
  "message_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
  "escalation_id": null,
  "simulation_time": "2026-09-15T12:01:00.000Z"
}
              

POST /v1/simulations/{simulationId}/reset

Retire a practice simulation and start a replacement.

Success status: 201

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111"
}
          

Example response

                {
  "retired_simulation_id": "11111111-1111-4111-8111-111111111111",
  "simulation": {
    "id": "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee",
    "purpose": "practice",
    "lifecycle": "active",
    "exercise_version": "1.0.0",
    "scenario_version": "1.0.0",
    "simulation_time": "2026-09-15T12:00:00.000Z"
  }
}
              

POST /v1/simulations/{simulationId}/checks

Run non-locking practice checks.

Success status: 201

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111"
}
          

Example response

                {
  "simulation_id": "11111111-1111-4111-8111-111111111111",
  "checked_at": "2026-09-15T12:00:00.000Z",
  "cases": [
    {
      "ticket_id": "22222222-2222-4222-8222-222222222222",
      "passed": false,
      "checks": [
        {
          "category": "REFUND_AMOUNT",
          "passed": false,
          "expected": {
            "kind": "money",
            "amount": 7999,
            "currency": "USD"
          },
          "actual": {
            "kind": "money",
            "amount": 0,
            "currency": "USD"
          },
          "remediation_code": "INSPECT_REFUNDS"
        }
      ]
    }
  ]
}
              

POST /v1/simulations/{simulationId}/submissions

Submit an assessment simulation for safe scoring.

Success status: 201

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111"
}
          

Example response

                {
  "submission_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
  "simulation_id": "11111111-1111-4111-8111-111111111111",
  "passed_cases": 1,
  "total_cases": 1,
  "percentage": 100,
  "cases": [
    {
      "ticket_id": "22222222-2222-4222-8222-222222222222",
      "passed": true,
      "actual_actions": [
        {
          "action": "refund_created",
          "refund_id": "77777777-7777-4777-8777-777777777777",
          "amount": 7999,
          "currency": "USD"
        },
        {
          "action": "ticket_closed",
          "resolution": "refund_completed"
        }
      ],
      "diagnostic_categories": [],
      "warnings": []
    }
  ],
  "diagnostics": {
    "duplicate_refunds": 0,
    "incorrect_refund_amount": 0,
    "missed_escalations": 0,
    "unnecessary_escalations": 0,
    "unresolved_tickets": 0
  },
  "warnings": [],
  "communication_quality_semantically_evaluated": false,
  "created_at": "2026-09-15T12:00:00.000Z"
}
              

GET /v1/simulations/{simulationId}/submissions/{submissionId}

Read an immutable safe submission report.

Success status: 200

Path parameters

            {
  "simulationId": "11111111-1111-4111-8111-111111111111",
  "submissionId": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"
}
          

Example response

                {
  "submission_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
  "simulation_id": "11111111-1111-4111-8111-111111111111",
  "passed_cases": 1,
  "total_cases": 1,
  "percentage": 100,
  "cases": [
    {
      "ticket_id": "22222222-2222-4222-8222-222222222222",
      "passed": true,
      "actual_actions": [],
      "diagnostic_categories": [],
      "warnings": []
    }
  ],
  "diagnostics": {
    "duplicate_refunds": 0,
    "incorrect_refund_amount": 0,
    "missed_escalations": 0,
    "unnecessary_escalations": 0,
    "unresolved_tickets": 0
  },
  "warnings": [],
  "communication_quality_semantically_evaluated": false,
  "created_at": "2026-09-15T12:00:00.000Z"
}