AI Minority Lab / API docs
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.
Pin
Versioned artifact
Guidance
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.
Guidance
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.
Guidance
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.
Guidance
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.
Guidance
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.
Guidance
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.
Guidance
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.
Guidance
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.
Contract
Documented requests
Every request below is exercised by an executable contract test against the pinned schemas.
enterprise.listSimulations
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"
}
]
}
enterprise.getSimulation
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"
}
enterprise.listTickets
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
}
enterprise.getTicket
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"
}
enterprise.getCustomer
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"
}
enterprise.getOrder
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"
}
enterprise.listShipments
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
}
]
enterprise.listRefunds
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"
}
]
enterprise.createRefund
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"
}
enterprise.listRefundPolicies
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
}
]
enterprise.listAuditEvents
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
}
enterprise.requestInformation
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"
}
enterprise.escalateTicket
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"
}
enterprise.closeTicket
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"
}
evaluation.resetPractice
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"
}
}
evaluation.checkPractice
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"
}
]
}
]
}
evaluation.submitAssessment
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"
}
evaluation.getSubmission
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"
}