403Webshell
Server IP : 35.80.110.71  /  Your IP : 216.73.216.221
Web Server : Apache/2.4.58 (Ubuntu)
System : Linux ip-172-31-21-44 6.17.0-1019-aws #19~24.04.1-Ubuntu SMP Tue Jun 23 18:53:06 UTC 2026 x86_64
User : ubuntu ( 1000)
PHP Version : 8.3.31
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/switchyard-web/public/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/switchyard-web/public/openapi.yaml
openapi: 3.1.0
info:
  title: Switchyard API
  version: 0.1.0
  description: |
    Multi-tenant ClickUp lead-intake API.

    Captures inquiries from many sources (brand contact forms, Upwork, Fiverr,
    direct email, generic webhooks), enriches them with service taxonomy +
    portfolio examples + a 0-100 lead score, and lands them as ClickUp tasks
    in a tenant's sales-pipeline list. ClickUp status changes mirror back via
    webhook so the local pipeline stays in lockstep.

    ## Authentication

    The bearer-API surface (`/v1/*` except `/v1/healthz`) takes an
    `Authorization: Bearer swy_…` token issued in the Filament admin. Tokens
    are workspace-scoped — every read/write happens against the workspace
    that owns the key.

    Inbound webhooks (`/webhooks/inbound/{workspace_id}/{source_slug}`) use a
    per-source HMAC signature in `X-Switchyard-Signature: t=<unix>,v1=<hex>`
    where `v1 = hmac_sha256(secret, "{t}.{body}")`. ClickUp webhooks
    (`/webhooks/clickup/{workspace_id}`) use ClickUp's own `X-Signature` HMAC
    against the per-workspace `webhook_secret`.

    ## Errors

    All error responses use RFC 7807 problem details (`application/problem+json`):
    `{ "type": "about:blank", "title": "...", "status": 4xx, "detail": "..." }`.

  contact:
    name: Switchyard Support
    url: https://switchyard.philiprehberger.com
  license:
    name: MIT

servers:
  - url: https://api.switchyard.philiprehberger.com
    description: Production

security:
  - bearerAuth: []

tags:
  - name: Health
    description: Service liveness
  - name: Leads
    description: Inbox + manual routing
  - name: Sources
    description: Configured inbound source channels
  - name: ClientProjects
    description: Phase 2 — client status board (projects, milestones, share tokens)
  - name: PublicStatus
    description: Phase 2 — public read-only status board (token-authenticated)
  - name: Templates
    description: Phase 3 — project templates + scaffold runs
  - name: Webhooks
    description: Signed inbound + ClickUp callback

paths:
  /v1/healthz:
    get:
      tags: [Health]
      operationId: healthz
      summary: Service healthcheck
      security: []
      responses:
        '200':
          description: Healthy
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Health' }
        '503':
          description: Unhealthy
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Health' }

  /v1/leads:
    get:
      tags: [Leads]
      operationId: listLeads
      summary: List leads
      description: Paginated list of leads in the caller's workspace. Filterable by status, source slug, and minimum score.
      parameters:
        - { in: query, name: status, schema: { type: string, enum: [received, enriched, routed, won, lost, dismissed] } }
        - { in: query, name: source, schema: { type: string }, description: Source slug (e.g. scopeforged-form) }
        - { in: query, name: min_score, schema: { type: integer, minimum: 0, maximum: 100 } }
        - { in: query, name: per_page, schema: { type: integer, minimum: 1, maximum: 100, default: 25 } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: '#/components/schemas/Lead' } }
                  meta: { $ref: '#/components/schemas/PaginationMeta' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      tags: [Leads]
      operationId: ingestLead
      summary: Ingest a lead
      description: Submit a raw payload from any configured source. Returns 202 + queues an IngestLeadJob.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [source_slug, payload]
              properties:
                source_slug: { type: string, description: Slug of a configured LeadSource }
                payload: { type: object, additionalProperties: true }
                external_ref: { type: string, nullable: true, maxLength: 128 }
      responses:
        '202':
          description: Accepted; job queued
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Accepted' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }

  /v1/leads/{id}:
    get:
      tags: [Leads]
      operationId: getLead
      summary: Get a lead
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/LeadWithRaw' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/leads/{id}/route:
    post:
      tags: [Leads]
      operationId: routeLead
      summary: Manually route a lead to ClickUp
      description: Idempotent on `clickup_task_id` — if the lead is already routed, returns the existing task reference without re-dispatching.
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        '202':
          description: Queued
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/Lead' }
                  queued: { type: boolean }
        '200':
          description: Already routed
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/Lead' }
                  already_routed: { type: boolean }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/leads/{id}/dismiss:
    post:
      tags: [Leads]
      operationId: dismissLead
      summary: Dismiss a lead
      description: Marks the lead as dismissed; the row is preserved for audit.
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        '200':
          description: Dismissed
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/Lead' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/sources:
    get:
      tags: [Sources]
      operationId: listSources
      summary: List configured lead sources
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: '#/components/schemas/Source' } }
        '401': { $ref: '#/components/responses/Unauthorized' }

  /v1/client-projects:
    get:
      tags: [ClientProjects]
      operationId: listClientProjects
      summary: List client projects (Phase 2)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: '#/components/schemas/ClientProject' } }
        '401': { $ref: '#/components/responses/Unauthorized' }
    post:
      tags: [ClientProjects]
      operationId: createClientProject
      summary: Create a client project (Phase 2)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [clickup_folder_id, name]
              properties:
                clickup_folder_id: { type: string, maxLength: 32, example: '901234567' }
                name: { type: string, maxLength: 255 }
                client_name: { type: string, nullable: true, maxLength: 255 }
                summary: { type: string, nullable: true, maxLength: 2000 }
                lead_id: { type: string, nullable: true, maxLength: 26 }
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/ClientProject' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }

  /v1/client-projects/{id}:
    get:
      tags: [ClientProjects]
      operationId: getClientProject
      summary: Get a client project with milestones (Phase 2)
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/ClientProjectWithMilestones' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
    patch:
      tags: [ClientProjects]
      operationId: updateClientProject
      summary: Update a client project (Phase 2)
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string, maxLength: 255 }
                client_name: { type: string, nullable: true, maxLength: 255 }
                summary: { type: string, nullable: true, maxLength: 2000 }
                is_archived: { type: boolean }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/ClientProject' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/client-projects/{id}/sync:
    post:
      tags: [ClientProjects]
      operationId: syncClientProject
      summary: Pull milestones from ClickUp into the local mirror (Phase 2)
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        '200':
          description: Synced
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/ClientProjectWithMilestones' }
                  sync:
                    type: object
                    properties:
                      lists: { type: integer }
                      tasks: { type: integer }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '502':
          description: ClickUp API rejected the sync
          content:
            application/problem+json:
              schema: { $ref: '#/components/schemas/Problem' }

  /v1/client-projects/{id}/tokens:
    post:
      tags: [ClientProjects]
      operationId: issueShareToken
      summary: Issue a public share link for a client project (Phase 2)
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                label: { type: string, nullable: true, maxLength: 120 }
                expires_at: { type: string, format: date-time, nullable: true }
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/IssuedShareToken' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/client-projects/{id}/tokens/{tokenId}:
    delete:
      tags: [ClientProjects]
      operationId: revokeShareToken
      summary: Revoke a public share link (Phase 2)
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
        - { in: path, name: tokenId, required: true, schema: { type: string } }
      responses:
        '200':
          description: Revoked (idempotent)
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id: { type: string }
                      revoked_at: { type: string, format: date-time, nullable: true }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/templates:
    get:
      tags: [Templates]
      operationId: listTemplates
      summary: List project templates (Phase 3)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: '#/components/schemas/ProjectTemplate' } }
        '401': { $ref: '#/components/responses/Unauthorized' }
    post:
      tags: [Templates]
      operationId: createTemplate
      summary: Create a project template (Phase 3)
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ProjectTemplateInput' }
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/ProjectTemplateWithStructure' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }

  /v1/templates/{id}:
    get:
      tags: [Templates]
      operationId: getTemplate
      summary: Get a template with structure (Phase 3)
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/ProjectTemplateWithStructure' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
    patch:
      tags: [Templates]
      operationId: updateTemplate
      summary: Update a project template (Phase 3)
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ProjectTemplateInput' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: '#/components/schemas/ProjectTemplateWithStructure' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      tags: [Templates]
      operationId: deleteTemplate
      summary: Delete a project template (Phase 3)
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id: { type: string }
                      deleted: { type: boolean }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/leads/{id}/scaffold:
    post:
      tags: [Templates]
      operationId: scaffoldFromLead
      summary: Manually scaffold a project for a lead (Phase 3)
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [template_id]
              properties:
                template_id: { type: string, maxLength: 26 }
      responses:
        '202':
          description: Queued
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      scaffold_run_id: { type: string }
                      status: { type: string, enum: [queued, running, completed, failed] }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }

  /v1/scaffold-runs:
    get:
      tags: [Templates]
      operationId: listScaffoldRuns
      summary: List recent scaffold runs (Phase 3)
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { type: array, items: { $ref: '#/components/schemas/ScaffoldRun' } }
        '401': { $ref: '#/components/responses/Unauthorized' }

  /public/status/{token}:
    get:
      tags: [PublicStatus]
      operationId: getPublicStatus
      summary: Public read-only client status board (Phase 2)
      description: |
        Returns the client-facing view of a tracked project: name, client name,
        summary, and the visible milestones. No bearer auth — the token itself
        is the secret. Per-IP rate-limited at 50/hour. Revoked / expired
        tokens return 410.
      security: []
      parameters:
        - { in: path, name: token, required: true, schema: { type: string, example: 'sty_…' } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PublicStatusResponse' }
        '404':
          description: Token unknown or project archived
          content:
            application/problem+json:
              schema: { $ref: '#/components/schemas/Problem' }
        '410':
          description: Token revoked or expired
          content:
            application/problem+json:
              schema: { $ref: '#/components/schemas/Problem' }
        '429':
          description: Per-IP rate limit exhausted
          content:
            application/problem+json:
              schema: { $ref: '#/components/schemas/Problem' }

  /webhooks/inbound/{workspace_id}/{source_slug}:
    post:
      tags: [Webhooks]
      operationId: inboundWebhook
      summary: Signed inbound webhook
      description: |
        Accept a signed payload from a configured source. The signature is
        verified using the source's `inbound_secret` (revealed once in the
        Filament admin). Optional `Idempotency-Key` header dedupes retries
        for 24 hours.
      security: []
      parameters:
        - { in: path, name: workspace_id, required: true, schema: { type: string } }
        - { in: path, name: source_slug, required: true, schema: { type: string } }
        - { in: header, name: X-Switchyard-Signature, required: true, schema: { type: string, example: 't=1750000000,v1=abc123…' } }
        - { in: header, name: Idempotency-Key, required: false, schema: { type: string, maxLength: 64 } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '202':
          description: Accepted; ingest job queued
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Accepted' }
        '200':
          description: Idempotency replay; cached response returned
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Accepted' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }

  /webhooks/clickup/{workspace_id}:
    post:
      tags: [Webhooks]
      operationId: clickupWebhook
      summary: ClickUp event callback
      description: |
        Receives task/status events from ClickUp. The signature in
        `X-Signature` is verified against the per-workspace `webhook_secret`
        on `clickup_connections`. On success, dispatches MirrorClickUpStatusJob
        to update the local lead mirror.
      security: []
      parameters:
        - { in: path, name: workspace_id, required: true, schema: { type: string } }
        - { in: header, name: X-Signature, required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Acknowledged
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: swy_<prefix>_<secret>

  schemas:
    Lead:
      type: object
      properties:
        id: { type: string, example: '01HZZ…' }
        status: { type: string, enum: [received, enriched, routed, won, lost, dismissed] }
        source_id: { type: string }
        external_ref: { type: string, nullable: true }
        parsed_contact:
          type: object
          properties:
            name: { type: string }
            email: { type: string, format: email }
            phone: { type: string }
            company: { type: string }
            linkedin: { type: string, format: uri }
          additionalProperties: false
        parsed_job:
          type: object
          properties:
            title: { type: string }
            body: { type: string }
            budget_text: { type: string }
            timeline_text: { type: string }
            urls: { type: array, items: { type: string, format: uri } }
          additionalProperties: false
        service_tag: { type: string, nullable: true, example: web-app }
        suggested_examples: { type: array, items: { type: string }, example: [highlands-ranch-champ, integridev] }
        lead_score: { type: integer, minimum: 0, maximum: 100 }
        clickup_task_id: { type: string, nullable: true }
        routed_at: { type: string, format: date-time, nullable: true }
        won_at: { type: string, format: date-time, nullable: true }
        lost_at: { type: string, format: date-time, nullable: true }
        dismissed_at: { type: string, format: date-time, nullable: true }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    LeadWithRaw:
      allOf:
        - $ref: '#/components/schemas/Lead'
        - type: object
          properties:
            raw_payload:
              type: object
              additionalProperties: true

    Source:
      type: object
      properties:
        id: { type: string }
        slug: { type: string, example: scopeforged-form }
        name: { type: string }
        kind: { type: string, enum: [webhook, email, manual] }
        enabled: { type: boolean }
        created_at: { type: string, format: date-time }

    PaginationMeta:
      type: object
      properties:
        total: { type: integer }
        per_page: { type: integer }
        current_page: { type: integer }
        last_page: { type: integer }

    Health:
      type: object
      properties:
        healthy: { type: boolean }
        version: { type: string }
        checks:
          type: object
          properties:
            db: { type: object, properties: { ok: { type: boolean }, error: { type: string } } }
            redis: { type: object, properties: { ok: { type: boolean }, error: { type: string } } }

    Accepted:
      type: object
      properties:
        ok: { type: boolean }
        accepted: { type: boolean }

    ClientProject:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        client_name: { type: string, nullable: true }
        summary: { type: string, nullable: true }
        clickup_folder_id: { type: string }
        is_archived: { type: boolean }
        last_synced_at: { type: string, format: date-time, nullable: true }
        created_at: { type: string, format: date-time }
        tokens:
          type: array
          nullable: true
          items: { $ref: '#/components/schemas/ShareTokenSummary' }

    ClientProjectWithMilestones:
      allOf:
        - $ref: '#/components/schemas/ClientProject'
        - type: object
          properties:
            milestones:
              type: array
              items: { $ref: '#/components/schemas/Milestone' }

    Milestone:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        clickup_task_id: { type: string, nullable: true }
        status_name: { type: string, nullable: true }
        status_color: { type: string, nullable: true, example: '#10b981' }
        status_type: { type: string, nullable: true, enum: [open, closed, custom] }
        is_visible: { type: boolean }
        is_complete: { type: boolean }
        display_order: { type: integer }
        due_at: { type: string, format: date-time, nullable: true }
        completed_at: { type: string, format: date-time, nullable: true }

    ShareTokenSummary:
      type: object
      properties:
        id: { type: string }
        label: { type: string, nullable: true }
        expires_at: { type: string, format: date-time, nullable: true }
        revoked_at: { type: string, format: date-time, nullable: true }
        last_viewed_at: { type: string, format: date-time, nullable: true }
        view_count: { type: integer }
        share_url: { type: string, format: uri, nullable: true }

    IssuedShareToken:
      type: object
      properties:
        id: { type: string }
        token: { type: string, example: 'sty_…' }
        label: { type: string, nullable: true }
        expires_at: { type: string, format: date-time, nullable: true }
        share_url: { type: string, format: uri }

    ProjectTemplate:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        service_tag: { type: string, nullable: true }
        clickup_space_id: { type: string }
        folder_name_pattern: { type: string }
        auto_scaffold: { type: boolean }
        created_at: { type: string, format: date-time }

    ProjectTemplateWithStructure:
      allOf:
        - $ref: '#/components/schemas/ProjectTemplate'
        - type: object
          properties:
            structure:
              type: object
              properties:
                lists:
                  type: array
                  items:
                    type: object
                    properties:
                      name: { type: string }
                      tasks:
                        type: array
                        items:
                          oneOf:
                            - { type: string }
                            - type: object
                              properties:
                                name: { type: string }
                                due_offset_days: { type: integer, nullable: true }
                                priority: { type: integer, nullable: true }
                                description: { type: string, nullable: true }

    ProjectTemplateInput:
      type: object
      required: [name, clickup_space_id, structure]
      properties:
        name: { type: string, maxLength: 120 }
        service_tag: { type: string, nullable: true, maxLength: 64 }
        clickup_space_id: { type: string, maxLength: 32 }
        folder_name_pattern: { type: string, nullable: true, maxLength: 255 }
        auto_scaffold: { type: boolean }
        structure:
          type: object
          required: [lists]
          properties:
            lists:
              type: array
              minItems: 1
              items:
                type: object
                properties:
                  name: { type: string }
                  tasks: { type: array, items: { oneOf: [{ type: string }, { type: object }] } }

    ScaffoldRun:
      type: object
      properties:
        id: { type: string }
        template_id: { type: string }
        lead_id: { type: string, nullable: true }
        client_project_id: { type: string, nullable: true }
        trigger: { type: string, example: 'lead.status=won' }
        status: { type: string, enum: [queued, running, completed, failed] }
        clickup_folder_id: { type: string, nullable: true }
        error_message: { type: string, nullable: true }
        started_at: { type: string, format: date-time, nullable: true }
        completed_at: { type: string, format: date-time, nullable: true }
        created_at: { type: string, format: date-time }

    PublicStatusResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            project:
              type: object
              properties:
                name: { type: string }
                client_name: { type: string, nullable: true }
                summary: { type: string, nullable: true }
                last_synced_at: { type: string, format: date-time, nullable: true }
            milestones:
              type: array
              items:
                type: object
                properties:
                  id: { type: string }
                  name: { type: string }
                  status_name: { type: string, nullable: true }
                  status_color: { type: string, nullable: true }
                  status_type: { type: string, nullable: true }
                  is_complete: { type: boolean }
                  due_at: { type: string, format: date-time, nullable: true }
                  completed_at: { type: string, format: date-time, nullable: true }

    Problem:
      type: object
      description: RFC 7807 problem details
      properties:
        type: { type: string, format: uri, example: 'about:blank' }
        title: { type: string }
        status: { type: integer }
        detail: { type: string }

  responses:
    BadRequest:
      description: Validation or shape error
      content:
        application/problem+json:
          schema: { $ref: '#/components/schemas/Problem' }
    Unauthorized:
      description: Missing / invalid bearer or signature
      content:
        application/problem+json:
          schema: { $ref: '#/components/schemas/Problem' }
    Forbidden:
      description: Scope or IP allowlist denied
      content:
        application/problem+json:
          schema: { $ref: '#/components/schemas/Problem' }
    NotFound:
      description: Resource not found
      content:
        application/problem+json:
          schema: { $ref: '#/components/schemas/Problem' }
    Conflict:
      description: Resource state conflict
      content:
        application/problem+json:
          schema: { $ref: '#/components/schemas/Problem' }

Youez - 2016 - github.com/yon3zu
LinuXploit