openapi: 3.0.3 info: title: IIOS — Insignia Interaction OS API version: "0.9.0" description: > REST API for the IIOS service. Everything is an Interaction inside a tenant scope, behind auth/consent/policy gates. Auth is a per-app Bearer JWT (mint dev tokens via POST /v1/dev/token when IIOS_DEV_TOKENS=1). Cross-tenant access returns 403. Companion docs: docs/IIOS_API_AND_SDK_GUIDE.md. servers: - url: http://localhost:3200 description: Local dev security: - bearerAuth: [] tags: - name: Health - name: Dev - name: Interactions - name: Threads - name: Inbox - name: Support - name: Adapters - name: Routing - name: AI - name: Calendar paths: /health: get: tags: [Health] summary: Liveness + egress provider bindings security: [] responses: "200": description: OK content: application/json: schema: type: object properties: status: { type: string, example: ok } db: { type: boolean } egressProviders: type: object additionalProperties: { type: string } example: { WEBHOOK: sandbox, EMAIL: http } /v1/dev/token: post: tags: [Dev] summary: Mint a dev JWT (requires IIOS_DEV_TOKENS=1) security: [] requestBody: required: true content: application/json: schema: type: object required: [appId, userId] properties: appId: { type: string, example: portal-demo } userId: { type: string, example: alice } name: { type: string } orgId: { type: string, description: "Tenant org; default org_", example: org_A } responses: "201": description: Token content: application/json: schema: type: object properties: { token: { type: string } } "400": { $ref: "#/components/responses/BadRequest" } /v1/dev/webhook/{channelType}: post: tags: [Dev] summary: Inject a server-signed simulated inbound webhook (dev only) security: [] parameters: - $ref: "#/components/parameters/channelType" requestBody: content: application/json: schema: type: object properties: from: { type: string } text: { type: string } responses: "201": { description: Raw inbound event accepted } /v1/interactions/ingest: post: tags: [Interactions] summary: Ingest a normalized interaction into the kernel (idempotent by providerEventId) requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/IngestRequest" } responses: "201": description: Created content: application/json: schema: type: object properties: { interactionId: { type: string } } "400": { $ref: "#/components/responses/BadRequest" } "401": { $ref: "#/components/responses/Unauthorized" } /v1/threads/{id}/messages: get: tags: [Threads] summary: List a thread's messages parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "200": description: OK content: application/json: schema: type: object properties: threadId: { type: string } messages: { type: array, items: { type: object } } "401": { $ref: "#/components/responses/Unauthorized" } post: tags: [Threads] summary: Send a message (REST fallback for the socket path) parameters: - $ref: "#/components/parameters/idPath" - name: idempotency-key in: header required: false schema: { type: string } requestBody: required: true content: application/json: schema: type: object required: [content] properties: content: { type: string } contentRef: { type: string } responses: "201": description: Message content: application/json: schema: { $ref: "#/components/schemas/Message" } "400": { $ref: "#/components/responses/BadRequest" } /v1/inbox/items: get: tags: [Inbox] summary: List inbox items for the caller parameters: - name: state in: query required: false schema: { $ref: "#/components/schemas/InboxState" } responses: "200": description: OK content: application/json: schema: { type: array, items: { $ref: "#/components/schemas/InboxItem" } } /v1/inbox/items/{id}: patch: tags: [Inbox] summary: Transition an inbox item parameters: [{ $ref: "#/components/parameters/idPath" }] requestBody: required: true content: application/json: schema: type: object required: [state] properties: state: { $ref: "#/components/schemas/InboxState" } reason: { type: string } responses: "200": description: OK content: application/json: schema: { $ref: "#/components/schemas/InboxItem" } "404": { $ref: "#/components/responses/NotFound" } /v1/support/tickets: post: tags: [Support] summary: Create a ticket requestBody: required: true content: application/json: schema: type: object required: [subject] properties: subject: { type: string } priority: { $ref: "#/components/schemas/TicketPriority" } threadId: { type: string } responses: "201": { description: Ticket, content: { application/json: { schema: { $ref: "#/components/schemas/Ticket" } } } } get: tags: [Support] summary: List tickets parameters: - name: scope in: query required: false schema: { type: string, enum: [mine, assigned], default: mine } responses: "200": { description: OK, content: { application/json: { schema: { type: array, items: { $ref: "#/components/schemas/Ticket" } } } } } /v1/support/escalate: post: tags: [Support] summary: Escalate a thread to support (opens a customer↔agent thread) requestBody: required: true content: application/json: schema: type: object required: [threadId] properties: threadId: { type: string } subject: { type: string } responses: "201": { description: Ticket, content: { application/json: { schema: { $ref: "#/components/schemas/Ticket" } } } } /v1/support/tickets/{id}: patch: tags: [Support] summary: Transition a ticket parameters: [{ $ref: "#/components/parameters/idPath" }] requestBody: required: true content: application/json: schema: type: object required: [state] properties: state: { $ref: "#/components/schemas/TicketState" } reason: { type: string } responses: "200": { description: Ticket, content: { application/json: { schema: { $ref: "#/components/schemas/Ticket" } } } } "404": { $ref: "#/components/responses/NotFound" } /v1/support/callbacks: post: tags: [Support] summary: Request a callback (bridges to a meeting) requestBody: required: true content: application/json: schema: type: object required: [preferChannel] properties: preferChannel: { type: string, example: PHONE } preferredTime: { type: string } notes: { type: string } ticketId: { type: string } responses: "201": { description: CallbackRequest, content: { application/json: { schema: { type: object } } } } /v1/support/queues: post: tags: [Support] summary: Create a support queue requestBody: required: true content: application/json: schema: { type: object, required: [name], properties: { name: { type: string } } } responses: "201": { description: Queue, content: { application/json: { schema: { type: object, properties: { id: { type: string } } } } } } /v1/support/queues/{id}/members: post: tags: [Support] summary: Add the caller to a queue parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "201": { description: Membership } /v1/support/agents/me/join: post: tags: [Support] summary: Join the default queue (go online) responses: "201": { description: Membership } /v1/support/members/me/availability: patch: tags: [Support] summary: Set the caller's availability requestBody: required: true content: application/json: schema: { type: object, required: [state], properties: { state: { type: string, example: AVAILABLE } } } responses: "200": { description: Membership } /v1/adapters/{channelType}/webhook: post: tags: [Adapters] summary: Provider inbound webhook (HMAC-signed; async normalize → 202) security: [] parameters: - $ref: "#/components/parameters/channelType" - name: x-iios-signature in: header required: true schema: { type: string, example: "sha256=..." } requestBody: required: true content: application/json: schema: { type: object, description: raw provider payload } responses: "202": { description: Accepted (normalized asynchronously) } "401": { description: Bad signature } /v1/adapters/{channelType}/send: post: tags: [Adapters] summary: Outbound send (through the Capability Broker; sandbox by default) parameters: [{ $ref: "#/components/parameters/channelType" }] requestBody: required: true content: application/json: schema: type: object required: [target] properties: target: { type: string } payload: { type: object } idempotencyKey: { type: string } responses: "201": { description: OutboundCommand, content: { application/json: { schema: { $ref: "#/components/schemas/OutboundCommand" } } } } /v1/adapters/inbound: get: tags: [Adapters] summary: List raw inbound events (+ interactionId once normalized) responses: "200": { description: OK, content: { application/json: { schema: { type: array, items: { type: object } } } } } /v1/adapters/outbound: get: tags: [Adapters] summary: List outbound commands (+ delivery attempts) responses: "200": { description: OK, content: { application/json: { schema: { type: array, items: { $ref: "#/components/schemas/OutboundCommand" } } } } } /v1/routes/bindings: post: tags: [Routing] summary: Create a route binding requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/CreateBinding" } responses: "201": { description: RouteBinding, content: { application/json: { schema: { $ref: "#/components/schemas/RouteBinding" } } } } get: tags: [Routing] summary: List bindings (your scope) responses: "200": { description: OK, content: { application/json: { schema: { type: array, items: { $ref: "#/components/schemas/RouteBinding" } } } } } /v1/routes/simulate: post: tags: [Routing] summary: Preview-first simulation (produces decisions, sends nothing) requestBody: required: true content: application/json: schema: type: object required: [interactionId, originChannelType] properties: interactionId: { type: string } originChannelType: { type: string } originRef: { type: string } responses: "201": description: Simulation content: application/json: schema: type: object properties: simulationId: { type: string } decisions: { type: array, items: { $ref: "#/components/schemas/RouteDecision" } } /v1/routes/decisions: get: tags: [Routing] summary: List route decisions (your scope) parameters: - name: state in: query required: false schema: { $ref: "#/components/schemas/RouteDecisionState" } responses: "200": { description: OK, content: { application/json: { schema: { type: array, items: { $ref: "#/components/schemas/RouteDecision" } } } } } /v1/routes/decisions/{id}/approve: post: tags: [Routing] summary: Approve a REVIEW decision (executes to sandbox) parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "201": { description: RouteDecision, content: { application/json: { schema: { $ref: "#/components/schemas/RouteDecision" } } } } "403": { $ref: "#/components/responses/Forbidden" } "404": { $ref: "#/components/responses/NotFound" } /v1/routes/decisions/{id}/deny: post: tags: [Routing] summary: Deny a decision parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "201": { description: RouteDecision, content: { application/json: { schema: { $ref: "#/components/schemas/RouteDecision" } } } } "403": { $ref: "#/components/responses/Forbidden" } /v1/routes/bindings/{id}/flush-digest: post: tags: [Routing] summary: Flush a binding's pending DIGEST decisions into one forward parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "201": { description: OK, content: { application/json: { schema: { type: object, properties: { forwarded: { type: integer } } } } } } /v1/ai/jobs: post: tags: [AI] summary: Run an AI job (proposes; never decides) requestBody: required: true content: application/json: schema: type: object required: [interactionId, jobType] properties: interactionId: { type: string } jobType: { $ref: "#/components/schemas/AiJobType" } responses: "201": description: Job + proposed artifact content: application/json: schema: type: object properties: job: { type: object } artifact: { $ref: "#/components/schemas/AiArtifact" } /v1/ai/artifacts: get: tags: [AI] summary: List AI artifacts (your scope) parameters: - name: interactionId in: query schema: { type: string } - name: status in: query schema: { $ref: "#/components/schemas/AiArtifactStatus" } responses: "200": { description: OK, content: { application/json: { schema: { type: array, items: { $ref: "#/components/schemas/AiArtifact" } } } } } /v1/ai/artifacts/{id}: get: tags: [AI] summary: Get an artifact (claims + evidence + modelRun) parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "200": { description: AiArtifact, content: { application/json: { schema: { $ref: "#/components/schemas/AiArtifact" } } } } "403": { $ref: "#/components/responses/Forbidden" } "404": { $ref: "#/components/responses/NotFound" } /v1/ai/artifacts/{id}/accept: post: tags: [AI] summary: Accept an artifact (human feedback) parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "201": { description: AiArtifact, content: { application/json: { schema: { $ref: "#/components/schemas/AiArtifact" } } } } "403": { $ref: "#/components/responses/Forbidden" } /v1/ai/artifacts/{id}/reject: post: tags: [AI] summary: Reject an artifact parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "201": { description: AiArtifact, content: { application/json: { schema: { $ref: "#/components/schemas/AiArtifact" } } } } "403": { $ref: "#/components/responses/Forbidden" } /v1/calendar/meetings: post: tags: [Calendar] summary: Schedule a meeting directly requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/ScheduleMeeting" } responses: "201": { description: Meeting, content: { application/json: { schema: { $ref: "#/components/schemas/Meeting" } } } } get: tags: [Calendar] summary: List meetings (your scope) responses: "200": { description: OK, content: { application/json: { schema: { type: array, items: { $ref: "#/components/schemas/Meeting" } } } } } /v1/calendar/requests: post: tags: [Calendar] summary: Create a meeting request (from a callback / AI claim / direct) parameters: - name: fromCallback in: query schema: { type: string } - name: fromClaim in: query schema: { type: string } requestBody: required: true content: application/json: schema: type: object required: [requestedWindow] properties: requestedWindow: { type: object } meetingType: { $ref: "#/components/schemas/MeetingType" } interactionId: { type: string } targetActorId: { type: string } responses: "201": { description: MeetingRequest, content: { application/json: { schema: { type: object } } } } /v1/calendar/meetings/{id}: get: tags: [Calendar] summary: Get a meeting (participants, transcripts, summaries, action items) parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "200": { description: Meeting, content: { application/json: { schema: { $ref: "#/components/schemas/Meeting" } } } } "403": { $ref: "#/components/responses/Forbidden" } "404": { $ref: "#/components/responses/NotFound" } /v1/calendar/meetings/{id}/consent: post: tags: [Calendar] summary: Set a participant's recording consent parameters: [{ $ref: "#/components/parameters/idPath" }] requestBody: required: true content: application/json: schema: type: object required: [actorRefId, status] properties: actorRefId: { type: string } status: { $ref: "#/components/schemas/ConsentStatus" } responses: "201": { description: Participant } "403": { $ref: "#/components/responses/Forbidden" } /v1/calendar/meetings/{id}/transcript: post: tags: [Calendar] summary: Generate a transcript (BLOCKED unless every attendee consented) parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "201": description: Transcript (status READY or BLOCKED) content: application/json: schema: type: object properties: status: { type: string, enum: [READY, BLOCKED, PENDING] } segments: { type: array, items: { type: object } } "403": { $ref: "#/components/responses/Forbidden" } /v1/calendar/meetings/{id}/summarize: post: tags: [Calendar] summary: Summarize a READY transcript (AI) + derive action items parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "201": { description: Meeting, content: { application/json: { schema: { $ref: "#/components/schemas/Meeting" } } } } "403": { $ref: "#/components/responses/Forbidden" } /v1/calendar/meetings/{id}/action-items: get: tags: [Calendar] summary: List meeting action items parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "200": { description: OK, content: { application/json: { schema: { type: array, items: { type: object } } } } } "403": { $ref: "#/components/responses/Forbidden" } /v1/calendar/providers: post: tags: [Calendar] summary: Connect a simulated calendar provider account responses: "201": { description: ProviderAccount, content: { application/json: { schema: { type: object, properties: { id: { type: string } } } } } } /v1/calendar/providers/{id}/sync: post: tags: [Calendar] summary: Sync the provider once (deterministic, no network) parameters: [{ $ref: "#/components/parameters/idPath" }] responses: "201": { description: OK, content: { application/json: { schema: { type: object, properties: { pulled: { type: integer }, cursor: { type: string } } } } } } components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT parameters: idPath: name: id in: path required: true schema: { type: string } channelType: name: channelType in: path required: true schema: { type: string, enum: [WEBHOOK, EMAIL, WHATSAPP, PORTAL] } responses: BadRequest: description: Validation error / missing auth / unknown field content: { application/json: { schema: { $ref: "#/components/schemas/Error" } } } Unauthorized: description: Invalid or missing token / bad signature content: { application/json: { schema: { $ref: "#/components/schemas/Error" } } } Forbidden: description: Cross-tenant access — resource not in your scope content: { application/json: { schema: { $ref: "#/components/schemas/Error" } } } NotFound: description: Not found (within your scope) content: { application/json: { schema: { $ref: "#/components/schemas/Error" } } } schemas: Error: type: object properties: statusCode: { type: integer } message: { type: string } error: { type: string } InboxState: { type: string, enum: [OPEN, SNOOZED, DONE, ARCHIVED, CANCELLED, STALE] } TicketState: { type: string, enum: [NEW, OPEN, PENDING, RESOLVED, CLOSED] } TicketPriority: { type: string, enum: [LOW, NORMAL, HIGH, URGENT] } RouteMode: { type: string, enum: [MANUAL, AUTOMATIC, HYBRID, SIMULATION_ONLY] } OutputFormat: { type: string, enum: [FORWARD, THREADED, DIGEST, SUMMARY, TRANSCRIPT] } RouteDecisionState: { type: string, enum: [ALLOW, DENY, REVIEW, SUPPRESS, SIMULATED] } AiJobType: { type: string, enum: [CLASSIFY, SUMMARIZE, EXTRACT] } AiArtifactStatus: { type: string, enum: [PROPOSED, ACCEPTED, REJECTED, SUPERSEDED] } MeetingType: { type: string, enum: [ZOOM, PHONE, IN_PERSON, CALLBACK, INTERNAL] } ConsentStatus: { type: string, enum: [UNKNOWN, GRANTED, DENIED, REVOKED] } IngestRequest: type: object required: [scope, channel, source, parts, occurredAt] properties: scope: type: object required: [orgId, appId] properties: orgId: { type: string } appId: { type: string } buId: { type: string } tenantId: { type: string } workspaceId: { type: string } projectId: { type: string } userScopeId: { type: string } channel: type: object required: [type] properties: type: { type: string } externalChannelId: { type: string } capabilityContract: { type: object } source: type: object required: [handleKind, externalId] properties: handleKind: { type: string } externalId: { type: string } displayName: { type: string } kind: { type: string } thread: type: object properties: externalThreadId: { type: string } subject: { type: string } parts: type: array items: type: object required: [kind] properties: kind: { type: string, example: TEXT } bodyText: { type: string } contentRef: { type: string } mimeType: { type: string } occurredAt: { type: string, format: date-time } providerEventId: { type: string } metadata: { type: object } Message: type: object properties: id: { type: string } threadId: { type: string } senderActorId: { type: string } content: { type: string } createdAt: { type: string, format: date-time } InboxItem: type: object properties: id: { type: string } ownerActorId: { type: string } kind: { type: string } state: { $ref: "#/components/schemas/InboxState" } title: { type: string } threadId: { type: string } Ticket: type: object properties: id: { type: string } subject: { type: string } state: { $ref: "#/components/schemas/TicketState" } priority: { $ref: "#/components/schemas/TicketPriority" } threadId: { type: string } assignedActorId: { type: string } OutboundCommand: type: object properties: id: { type: string } channelType: { type: string } target: { type: string } status: { type: string, enum: [PENDING, SENT, FAILED, RATE_LIMITED, BLOCKED] } providerRef: { type: string } consentReceiptRef: { type: string } idempotencyKey: { type: string } CreateBinding: type: object required: [originChannelType, destinationChannelType] properties: originChannelType: { type: string } originRef: { type: string } destinationChannelType: { type: string } destinationRef: { type: string } restrictionProfile: { type: string, example: CHILD } mode: { $ref: "#/components/schemas/RouteMode" } outputFormat: { $ref: "#/components/schemas/OutputFormat" } frequency: { type: string } includeAttachments: { type: boolean } requiresReview: { type: boolean } enabled: { type: boolean } RouteBinding: allOf: - $ref: "#/components/schemas/CreateBinding" - type: object properties: id: { type: string } RouteDecision: type: object properties: id: { type: string } interactionId: { type: string } routeBindingId: { type: string } decisionState: { $ref: "#/components/schemas/RouteDecisionState" } reasonCodes: { type: array, items: { type: string } } previewPayload: { type: object } outputFormat: { $ref: "#/components/schemas/OutputFormat" } executed: { type: boolean } AiArtifact: type: object properties: id: { type: string } jobId: { type: string } artifactType: { type: string, enum: [CLASSIFICATION, SUMMARY, TRANSCRIPT, DIGEST, EXTRACTION] } status: { $ref: "#/components/schemas/AiArtifactStatus" } confidence: { type: number, nullable: true } contentRef: { type: object } abstentionReason: { type: string, nullable: true } claims: { type: array, items: { type: object } } evidence: { type: array, items: { type: object } } modelRun: { type: object } ScheduleMeeting: type: object required: [meetingType, title, startAt] properties: meetingType: { $ref: "#/components/schemas/MeetingType" } title: { type: string } startAt: { type: string, format: date-time } endAt: { type: string, format: date-time } timezone: { type: string } requestId: { type: string } attendees: type: array items: type: object required: [userId] properties: userId: { type: string } displayName: { type: string } role: { type: string, enum: [REQUIRED, OPTIONAL] } visibility: { type: string, enum: [FULL, LIMITED, NONE] } Meeting: type: object properties: id: { type: string } meetingType: { $ref: "#/components/schemas/MeetingType" } status: { type: string, enum: [REQUESTED, SCHEDULED, CONFIRMED, CANCELLED, COMPLETED, NO_SHOW] } title: { type: string } organizerActorId: { type: string } startAt: { type: string, format: date-time, nullable: true } timezone: { type: string } participants: { type: array, items: { type: object } }