TenantAtlas/specs/256-external-support-desk-handoff/contracts/external-support-desk-handoff.logical.openapi.yaml
ahmido 52ebf63af1
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 2m6s
feat(specs/256): external support desk handoff (#301)
Implement external support desk handoff (spec 256). Created and pushed branch `256-external-support-desk-handoff`.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #301
2026-04-29 20:16:40 +00:00

216 lines
7.2 KiB
YAML

openapi: 3.0.3
info:
title: TenantPilot Admin — External Support Desk Handoff (Conceptual)
version: 0.1.0
description: |
Conceptual contract for the first external support desk handoff slice.
NOTE: These flows are implemented as Filament (Livewire) actions on
existing pages. This file captures the expected action payload, outcome
semantics, and authorization boundaries rather than a public REST API.
servers:
- url: /admin
paths:
/t/{tenant}/support-requests/actions/submit:
post:
summary: Submit a tenant-context support request with optional external handoff
description: |
Existing tenant dashboard support action, extended with one-way external
handoff behavior.
Authorization:
- Workspace non-member or non-entitled tenant actor: 404
- Entitled tenant member without `support_requests.create`: 403
- Authorized actor: 200 with one support-request submission result
Behavior:
- Always creates the internal `SR-...` support request first
- `internal_only` performs no outbound handoff
- `link_existing_ticket` stores the provided external reference and must not call external create
- `create_external_ticket` uses one application-configured external target only
- `create_external_ticket` applies a maximum 5 second outbound timeout budget
- External create failure keeps the internal support request and returns an explicit failed-handoff outcome
- No queue, `OperationRun`, retry scheduler, or bidirectional sync is introduced
parameters:
- name: tenant
in: path
required: true
schema:
type: string
description: Filament tenancy slug (`tenants.external_id`)
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SupportRequestHandoffSubmission'
responses:
'200':
description: Support request accepted with internal-only, linked, created, or failed-handoff outcome
content:
application/json:
schema:
$ref: '#/components/schemas/SupportRequestHandoffResult'
'403':
description: Forbidden (entitled tenant member lacks support-request capability)
'404':
description: Not found (wrong workspace, non-member, or missing tenant entitlement)
/operations/{run}/support-requests/actions/submit:
post:
summary: Submit a run-context support request with optional external handoff
description: |
Existing canonical run detail support action, extended with one-way
external handoff behavior.
Authorization:
- Inaccessible run or run outside entitled tenant scope: 404
- Entitled member without `support_requests.create`: 403
- Authorized actor: 200 with one support-request submission result
Behavior:
- The run must resolve to an entitled tenant before any support truth is revealed
- Uses the same payload contract and outcome semantics as the tenant-context action
- Does not create, resume, or update an `OperationRun`
parameters:
- name: run
in: path
required: true
schema:
type: integer
description: Internal `operation_runs.id`
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SupportRequestHandoffSubmission'
responses:
'200':
description: Support request accepted with internal-only, linked, created, or failed-handoff outcome
content:
application/json:
schema:
$ref: '#/components/schemas/SupportRequestHandoffResult'
'403':
description: Forbidden (entitled member lacks support-request capability)
'404':
description: Not found (run inaccessible under workspace or tenant scope)
components:
schemas:
SupportRequestHandoffSubmission:
type: object
required:
- severity
- summary
- handoff_mode
properties:
severity:
type: string
enum: [low, normal, high, blocking]
summary:
type: string
reproduction_notes:
type: string
nullable: true
contact_name:
type: string
nullable: true
contact_email:
type: string
format: email
nullable: true
handoff_mode:
type: string
enum: [internal_only, create_external_ticket, link_existing_ticket]
external_ticket_reference:
type: string
nullable: true
description: Required when `handoff_mode = link_existing_ticket`
external_ticket_url:
type: string
format: uri
nullable: true
target_available:
type: boolean
nullable: true
description: Derived UI hint only; the server remains authoritative
SupportRequestHandoffResult:
type: object
required:
- support_request_id
- internal_reference
- primary_context_type
- handoff_mode
- handoff_outcome
- latest_summary
properties:
support_request_id:
type: integer
internal_reference:
type: string
primary_context_type:
type: string
enum: [tenant, operation_run]
primary_context_id:
type: integer
nullable: true
handoff_mode:
type: string
enum: [internal_only, create_external_ticket, link_existing_ticket]
handoff_outcome:
type: string
enum:
- internal_only
- external_ticket_created
- external_ticket_linked
- external_handoff_failed
external_ticket_reference:
type: string
nullable: true
external_ticket_url:
type: string
format: uri
nullable: true
failure_summary:
type: string
nullable: true
latest_summary:
$ref: '#/components/schemas/LatestSupportRequestHandoffSummary'
LatestSupportRequestHandoffSummary:
type: object
required:
- internal_reference
- primary_context_type
- submitted_at
- handoff_mode
- has_external_link
- has_failure
properties:
internal_reference:
type: string
primary_context_type:
type: string
enum: [tenant, operation_run]
primary_context_id:
type: integer
nullable: true
submitted_at:
type: string
format: date-time
handoff_mode:
type: string
enum: [internal_only, create_external_ticket, link_existing_ticket]
external_ticket_reference:
type: string
nullable: true
external_ticket_url:
type: string
format: uri
nullable: true
external_handoff_failure_summary:
type: string
nullable: true
has_external_link:
type: boolean
has_failure:
type: boolean