openapi: 3.2.0 info: title: mcpgate-backend Support Gateway API version: 0.4.20 tags: - name: support-gateway paths: /support/enroll: post: tags: - support-gateway summary: Enroll description: 'Enroll a new gateway (first contact) or update an existing one. See D21 for the full state machine: - no token + URL unseen → 201 Created + new (customer_id, instance_token) - no token + URL exists → 409 enrollment_conflict - token matches row → 200 OK, update mutable fields - token present, no row → 403 invalid_instance_token' operationId: enroll_support_enroll_post parameters: - name: X-Instance-Token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Instance-Token requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EnrollRequest' responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /support/sessions: post: tags: - support-gateway summary: Start Session description: Customer toggles support ON. Triggers D30 email. operationId: start_session_support_sessions_post parameters: - name: X-Instance-Token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Instance-Token requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SessionStartRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SessionStartResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /support/sessions/{session_id}: delete: tags: - support-gateway summary: End Session description: Customer-side revoke. operationId: end_session_support_sessions__session_id__delete parameters: - name: session_id in: path required: true schema: type: integer minimum: 1 title: Session Id - name: X-Instance-Token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Instance-Token requestBody: content: application/json: schema: $ref: '#/components/schemas/SessionCloseRequest' default: reason: revoked responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SessionCloseResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /support/sessions/{session_id}/extend: post: tags: - support-gateway summary: Extend Session description: 'D32 auto-extend, clamped by C2 hard cap. Returns ``extended=False`` if the hard cap is already reached — the gateway should treat that as a signal to display the "session capped" notice rather than silently retry.' operationId: extend_session_support_sessions__session_id__extend_post parameters: - name: session_id in: path required: true schema: type: integer minimum: 1 title: Session Id - name: X-Instance-Token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Instance-Token requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SessionExtendRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SessionExtendResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /support/sessions/{session_id}/close: post: tags: - support-gateway summary: Close Session From Gateway description: 'Gateway-initiated close (idle timeout or expiry). Same effect as DELETE, separate route for clarity in audit logs and so the gateway-side state machine can pass a more honest ``closed_reason``.' operationId: close_session_from_gateway_support_sessions__session_id__close_post parameters: - name: session_id in: path required: true schema: type: integer minimum: 1 title: Session Id - name: X-Instance-Token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Instance-Token requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SessionCloseRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SessionCloseResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /support/messages: post: tags: - support-gateway summary: Send Message From Customer operationId: send_message_from_customer_support_messages_post parameters: - name: X-Instance-Token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Instance-Token requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MessageRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MessageOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' get: tags: - support-gateway summary: List Messages For Customer description: 'Gateway pulls fresh staff messages (N2 fallback path). Only returns staff → customer messages; the gateway is already canonical for its own outbound messages so it doesn''t need to read them back from us.' operationId: list_messages_for_customer_support_messages_get parameters: - name: since in: query required: false schema: anyOf: - type: string format: date-time - type: 'null' title: Since - name: limit in: query required: false schema: type: integer maximum: 500 minimum: 1 default: 100 title: Limit - name: X-Instance-Token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Instance-Token responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MessageListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /support/messages/read: post: tags: - support-gateway summary: Mark Staff Messages Read By Customer description: 'Customer''s browser viewed staff replies — stamp customer_read_at. Only flips messages that: - belong to this instance''s customer, - are direction=staff→customer (the only ones the customer "reads"), - have customer_read_at currently NULL. Idempotent: replaying the same id list is a no-op.' operationId: mark_staff_messages_read_by_customer_support_messages_read_post parameters: - name: X-Instance-Token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Instance-Token requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MarkMessagesReadRequest' responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /support/audit_meta: post: tags: - support-gateway summary: Push Audit Meta operationId: push_audit_meta_support_audit_meta_post parameters: - name: X-Instance-Token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Instance-Token requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AuditMetaRequest' responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /support/subscriptions/consent/{token}: get: tags: - support-gateway summary: Confirm Subscription Consent description: 'DOI opt-in landing page for support-driven bug subscriptions. Verifies the signed token, flips the named subscription to ``active``, and back-fills all the customer''s other pending subscriptions so they don''t have to click a separate link per bug. Idempotent: clicking twice keeps the same ``consented_at``.' operationId: confirm_subscription_consent_support_subscriptions_consent__token__get parameters: - name: token in: path required: true schema: type: string title: Token responses: '200': description: Successful Response content: text/html: schema: type: string '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /support/notifications/unsubscribe/{token}: get: tags: - support-gateway summary: Unsubscribe From Fix Notifications description: 'Per-subscription opt-out triggered from the fix-released email footer. Sets ``opted_out_at`` on the single subscription — the release fan-out skips ``opted_out`` rows. Customer-level consent stays on file so future subscriptions still auto-activate (granular opt-out, not blanket).' operationId: unsubscribe_from_fix_notifications_support_notifications_unsubscribe__token__get parameters: - name: token in: path required: true schema: type: string title: Token responses: '200': description: Successful Response content: text/html: schema: type: string '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' components: schemas: MessageOut: properties: id: type: integer title: Id session_id: anyOf: - type: integer - type: 'null' title: Session Id direction: type: string enum: - customer→staff - staff→customer title: Direction author_id: type: string title: Author Id body: type: string title: Body ts: type: string format: date-time title: Ts customer_read_at: anyOf: - type: string format: date-time - type: 'null' title: Customer Read At staff_read_at: anyOf: - type: string format: date-time - type: 'null' title: Staff Read At type: object required: - id - session_id - direction - author_id - body - ts title: MessageOut HTTPValidationError: properties: detail: items: $ref: '#/components/schemas/ValidationError' type: array title: Detail type: object title: HTTPValidationError MarkMessagesReadRequest: properties: message_ids: items: type: integer type: array maxItems: 500 minItems: 1 title: Message Ids type: object required: - message_ids title: MarkMessagesReadRequest description: 'Gateway → Backend: customer''s browser has opened the dashboard and seen the listed staff messages. Backend stamps customer_read_at so the staff side flips the second tick.' SessionCloseResponse: properties: session_id: type: integer title: Session Id closed_at: type: string format: date-time title: Closed At type: object required: - session_id - closed_at title: SessionCloseResponse SessionStartRequest: properties: ttl_minutes: type: integer enum: - 60 - 240 - 1440 title: Ttl Minutes initial_message: type: string maxLength: 10000 minLength: 1 title: Initial Message type: object required: - ttl_minutes - initial_message title: SessionStartRequest SessionCloseRequest: properties: reason: type: string enum: - revoked - expired - idle_timeout title: Reason default: revoked type: object title: SessionCloseRequest MessageListResponse: properties: messages: items: $ref: '#/components/schemas/MessageOut' type: array title: Messages type: object required: - messages title: MessageListResponse MessageRequest: properties: body: type: string maxLength: 10000 minLength: 1 title: Body session_id: anyOf: - type: integer - type: 'null' title: Session Id type: object required: - body title: MessageRequest EnrollRequest: properties: instance_url: type: string maxLength: 2083 minLength: 1 format: uri title: Instance Url owner_email: type: string format: email title: Owner Email display_name: type: string maxLength: 255 minLength: 1 title: Display Name type: object required: - instance_url - owner_email - display_name title: EnrollRequest SessionStartResponse: properties: session_id: type: integer title: Session Id started_at: type: string format: date-time title: Started At expires_at: type: string format: date-time title: Expires At max_expires_at: type: string format: date-time title: Max Expires At initial_message_id: anyOf: - type: integer - type: 'null' title: Initial Message Id type: object required: - session_id - started_at - expires_at - max_expires_at title: SessionStartResponse SessionExtendResponse: properties: session_id: type: integer title: Session Id expires_at: type: string format: date-time title: Expires At max_expires_at: type: string format: date-time title: Max Expires At extended: type: boolean title: Extended type: object required: - session_id - expires_at - max_expires_at - extended title: SessionExtendResponse AuditMetaRequest: properties: ts: type: string format: date-time title: Ts staff_id: type: string maxLength: 64 minLength: 1 title: Staff Id action: type: string maxLength: 64 minLength: 1 title: Action response_status: anyOf: - type: integer - type: 'null' title: Response Status response_size: anyOf: - type: integer - type: 'null' title: Response Size latency_ms: anyOf: - type: integer - type: 'null' title: Latency Ms type: object required: - ts - staff_id - action title: AuditMetaRequest SessionExtendRequest: properties: extend_minutes: type: integer maximum: 120.0 minimum: 1.0 title: Extend Minutes default: 15 type: object title: SessionExtendRequest ValidationError: properties: loc: items: anyOf: - type: string - type: integer type: array title: Location msg: type: string title: Message type: type: string title: Error Type input: title: Input ctx: type: object title: Context type: object required: - loc - msg - type title: ValidationError