openapi: 3.0.3
info:
  title: traxy Customer API and MCP V1
  version: 1.0.0
  description: |
    Public customer-facing read API, Ordinal Audience Analytics endpoints, and
    remote MCP Streamable HTTP endpoint. Customer REST and Ordinal requests use
    bearer API keys. MCP uses OAuth access tokens issued for the traxy MCP
    resource; Customer API keys are never accepted by MCP. Public REST clients must
    not send organizationId. Default API keys cover the general read API.
    Ordinal Audience Analytics is self-serve for paid workspaces and requires
    explicitly selected audience_analytics scopes on the key.
    The MCP surface exposes scope-gated tools through tools/list, including
    agent:read tools for safe automated lead-finding agent visibility. Latest
    MCP protocol version is 2025-11-25.
servers:
  - url: https://api.traxy.ai/api/v1
    description: Production
security:
  - CustomerApiKey: []
tags:
  - name: Customer REST
  - name: Ordinal Audience Analytics
  - name: MCP OAuth
  - name: MCP
paths:
  /customer/analytics/meetings:
    get:
      tags:
        - Customer REST
      summary: Get Meetings booked attribution counts.
      description: Uses the credential workspace and verifies its creator still has
        workspace read access. The feed omits meetingWith unless
        leads:contact_read is granted; source calendars and provider payloads are
        never returned.
      security:
        - CustomerApiKey:
            - analytics:read
      parameters:
        - &a1
          in: query
          name: range
          schema:
            type: string
            enum:
              - 7d
              - 30d
              - 90d
              - all
            default: 30d
      responses:
        "200":
          description: Meetings read result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerMeetingsSummary"
        "400":
          $ref: "#/components/responses/ValidationFailed"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: Read capability or active workspace membership required.
        "503":
          description: Meetings temporarily unavailable; retry after the Retry-After header.
          headers:
            Retry-After:
              schema:
                type: integer
                example: 1
  /customer/meetings:
    get:
      tags:
        - Customer REST
      summary: List attributed Meetings with snapshot cursor pagination.
      description: Uses the credential workspace and verifies its creator still has
        workspace read access. The feed omits meetingWith unless
        leads:contact_read is granted; source calendars and provider payloads are
        never returned.
      security:
        - CustomerApiKey:
            - leads:read
      parameters:
        - *a1
        - in: query
          name: limit
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - in: query
          name: cursor
          schema:
            type: string
            maxLength: 512
        - in: query
          name: leadId
          schema:
            type: string
            format: uuid
        - in: query
          name: targetType
          schema:
            type: string
            enum:
              - PERSON
              - COMPANY
      responses:
        "200":
          description: Meetings read result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomerMeetingsFeed"
        "400":
          $ref: "#/components/responses/ValidationFailed"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: Read capability or active workspace membership required.
        "503":
          description: Meetings temporarily unavailable; retry after the Retry-After header.
          headers:
            Retry-After:
              schema:
                type: integer
                example: 1
  /customer/workspace-access:
    get:
      tags: [Customer REST]
      summary: Get business-facing workspace data access.
      security:
        - CustomerApiKey: [workspace:read]
      responses:
        '200':
          description: API-visible workspace capabilities and usage.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkspaceAccess'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
        '429':
          $ref: '#/components/responses/RateLimited'
  /customer/leads:
    get:
      tags: [Customer REST]
      summary: List API-visible leads.
      security:
        - CustomerApiKey: [leads:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/LeadTierFilter'
        - $ref: '#/components/parameters/LeadTiersFilter'
        - $ref: '#/components/parameters/LeadDispositionFilter'
        - $ref: '#/components/parameters/LeadDispositionsFilter'
        - $ref: '#/components/parameters/MinIcpMatchPercentFilter'
        - $ref: '#/components/parameters/MaxIcpMatchPercentFilter'
        - $ref: '#/components/parameters/MonitoredProfileIdFilter'
        - $ref: '#/components/parameters/MonitoredProfileIdsFilter'
        - $ref: '#/components/parameters/PostIdFilter'
        - $ref: '#/components/parameters/EngagementTypeFilter'
        - $ref: '#/components/parameters/EngagementTypesFilter'
        - $ref: '#/components/parameters/OwnerIdFilter'
        - $ref: '#/components/parameters/OwnerIdsFilter'
        - $ref: '#/components/parameters/DateFromFilter'
        - $ref: '#/components/parameters/DateToFilter'
        - $ref: '#/components/parameters/SearchQueryFilter'
        - $ref: '#/components/parameters/CompanyNamesFilter'
        - $ref: '#/components/parameters/LeadOriginFilter'
      responses:
        '200':
          description: Lead page. Contact fields are null unless the credential has leads:contact_read.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LeadPage'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
        '429':
          $ref: '#/components/responses/RateLimited'
  /customer/leads/signals:
    get:
      tags: [Customer REST]
      summary: List visible lead signals with safe lead, post, and watched-profile context.
      security:
        - CustomerApiKey: [leads:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/LeadIdFilter'
        - $ref: '#/components/parameters/MonitoredProfileIdFilter'
        - $ref: '#/components/parameters/MonitoredProfileIdsFilter'
        - $ref: '#/components/parameters/PostIdFilter'
        - $ref: '#/components/parameters/EngagementTypeFilter'
        - $ref: '#/components/parameters/EngagementTypesFilter'
        - $ref: '#/components/parameters/DateFromFilter'
        - $ref: '#/components/parameters/DateToFilter'
        - $ref: '#/components/parameters/MinIcpMatchPercentFilter'
        - $ref: '#/components/parameters/MaxIcpMatchPercentFilter'
        - $ref: '#/components/parameters/LeadDispositionFilter'
        - $ref: '#/components/parameters/SearchQueryFilter'
        - $ref: '#/components/parameters/CompanyNamesFilter'
        - $ref: '#/components/parameters/ProfileTypeFilter'
        - $ref: '#/components/parameters/RelationshipLabelFilter'
        - $ref: '#/components/parameters/LeadOriginFilter'
      responses:
        '200':
          description: Lead engagement page with a nullable canonical signal on each record and lead.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LeadSignalPage'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
        '429':
          $ref: '#/components/responses/RateLimited'
  /customer/leads/counts:
    get:
      tags: [Customer REST]
      summary: Get API-visible lead counts.
      security:
        - CustomerApiKey: [leads:read]
      responses:
        '200':
          description: Lead counts grouped by tier/status.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
  /customer/leads/stats:
    get:
      tags: [Customer REST]
      summary: Get aggregate lead stats.
      security:
        - CustomerApiKey: [leads:read]
      responses:
        '200':
          description: Aggregate lead stats.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
  /customer/leads/{leadId}:
    get:
      tags: [Customer REST]
      summary: Get one API-visible lead.
      security:
        - CustomerApiKey: [leads:read]
      parameters:
        - $ref: '#/components/parameters/LeadId'
      responses:
        '200':
          description: Lead detail.
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    $ref: '#/components/schemas/Lead'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
        '404':
          $ref: '#/components/responses/NotFound'
  /customer/leads/{leadId}/engagements:
    get:
      tags: [Customer REST]
      summary: List safe engagement summaries for one lead.
      security:
        - CustomerApiKey: [leads:read]
      parameters:
        - $ref: '#/components/parameters/LeadId'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/EngagementTypeFilter'
        - $ref: '#/components/parameters/MonitoredProfileIdFilter'
        - $ref: '#/components/parameters/PostIdFilter'
        - $ref: '#/components/parameters/DateFromFilter'
        - $ref: '#/components/parameters/DateToFilter'
      responses:
        '200':
          description: Engagement page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PageEnvelope'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /customer/icp:
    get:
      tags: [Customer REST]
      summary: Get active ICP configuration.
      security:
        - CustomerApiKey: [icp:read]
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
  /customer/icp/active:
    get:
      tags: [Customer REST]
      summary: Get active ICP configuration through the active alias.
      security:
        - CustomerApiKey: [icp:read]
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
  /customer/watchlist/profiles:
    get:
      tags: [Customer REST]
      summary: List watchlist profiles.
      security:
        - CustomerApiKey: [watchlist:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/ProfileTypeFilter'
        - $ref: '#/components/parameters/ProfileTypesFilter'
        - $ref: '#/components/parameters/MonitoredProfileSourceFilter'
        - $ref: '#/components/parameters/SlotKindFilter'
        - $ref: '#/components/parameters/RelationshipLabelFilter'
        - $ref: '#/components/parameters/WatchlistStateFilter'
        - $ref: '#/components/parameters/SearchQueryFilter'
        - $ref: '#/components/parameters/WatchlistSortFilter'
        - $ref: '#/components/parameters/SortDirectionFilter'
      responses:
        '200':
          $ref: '#/components/responses/PageObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
  /customer/watchlist/profiles/{monitoredProfileId}:
    get:
      tags: [Customer REST]
      summary: Get one monitored profile with safe relationship and follower metadata.
      security:
        - CustomerApiKey: [watchlist:read]
      parameters:
        - $ref: '#/components/parameters/MonitoredProfileIdPath'
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
        '404':
          $ref: '#/components/responses/NotFound'
  /customer/watchlist/summary:
    get:
      tags: [Customer REST]
      summary: Get watchlist summary.
      security:
        - CustomerApiKey: [watchlist:read]
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /customer/watchlist/recommendations:
    get:
      tags: [Customer REST]
      summary: List watchlist recommendations.
      security:
        - CustomerApiKey: [watchlist:read]
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /customer/analytics/dashboard:
    get:
      tags: [Customer REST]
      summary: Get aggregate analytics dashboard.
      security:
        - CustomerApiKey: [analytics:read]
      parameters:
        - $ref: '#/components/parameters/Range'
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /customer/analytics/posts:
    get:
      tags: [Customer REST]
      summary: List post analytics, including safe postUrl and contentText.
      security:
        - CustomerApiKey: [analytics:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/MonitoredProfileIdFilter'
        - $ref: '#/components/parameters/MonitoredProfileIdsFilter'
        - $ref: '#/components/parameters/ProfileTypeFilter'
        - $ref: '#/components/parameters/RelationshipLabelFilter'
        - $ref: '#/components/parameters/MonitoredProfileSourceFilter'
        - $ref: '#/components/parameters/DateFromFilter'
        - $ref: '#/components/parameters/DateToFilter'
        - $ref: '#/components/parameters/MinIcpMatchPercentFilter'
        - $ref: '#/components/parameters/ContentStatusFilter'
        - $ref: '#/components/parameters/SearchQueryFilter'
        - $ref: '#/components/parameters/ContentSortFilter'
        - $ref: '#/components/parameters/SortDirectionFilter'
      responses:
        '200':
          $ref: '#/components/responses/PageObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /customer/analytics/posts/{postId}/summary:
    get:
      tags: [Customer REST]
      summary: Get one post summary, including safe postUrl and contentText.
      security:
        - CustomerApiKey: [analytics:read]
      parameters:
        - $ref: '#/components/parameters/PostId'
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '404':
          $ref: '#/components/responses/NotFound'
  /customer/analytics/posts/{postId}/qualified-leads:
    get:
      tags: [Customer REST]
      summary: List qualified leads for one post.
      security:
        - CustomerApiKey: [analytics:read]
      parameters:
        - $ref: '#/components/parameters/PostId'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          $ref: '#/components/responses/PageObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '404':
          $ref: '#/components/responses/NotFound'
  /customer/content-feed:
    get:
      tags: [Customer REST]
      summary: List content feed posts, including safe postUrl and contentText.
      security:
        - CustomerApiKey: [content:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/MonitoredProfileIdFilter'
        - $ref: '#/components/parameters/MonitoredProfileIdsFilter'
        - $ref: '#/components/parameters/ProfileTypeFilter'
        - $ref: '#/components/parameters/RelationshipLabelFilter'
        - $ref: '#/components/parameters/MonitoredProfileSourceFilter'
        - $ref: '#/components/parameters/DateFromFilter'
        - $ref: '#/components/parameters/DateToFilter'
        - $ref: '#/components/parameters/MinIcpMatchPercentFilter'
        - $ref: '#/components/parameters/ContentStatusFilter'
        - $ref: '#/components/parameters/SearchQueryFilter'
        - $ref: '#/components/parameters/ContentSortFilter'
        - $ref: '#/components/parameters/SortDirectionFilter'
      responses:
        '200':
          $ref: '#/components/responses/PageObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /customer/content/feed:
    get:
      tags: [Customer REST]
      summary: List content feed posts through the nested alias.
      security:
        - CustomerApiKey: [content:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/MonitoredProfileIdFilter'
        - $ref: '#/components/parameters/MonitoredProfileIdsFilter'
        - $ref: '#/components/parameters/ProfileTypeFilter'
        - $ref: '#/components/parameters/RelationshipLabelFilter'
        - $ref: '#/components/parameters/MonitoredProfileSourceFilter'
        - $ref: '#/components/parameters/DateFromFilter'
        - $ref: '#/components/parameters/DateToFilter'
        - $ref: '#/components/parameters/MinIcpMatchPercentFilter'
        - $ref: '#/components/parameters/ContentStatusFilter'
        - $ref: '#/components/parameters/SearchQueryFilter'
        - $ref: '#/components/parameters/ContentSortFilter'
        - $ref: '#/components/parameters/SortDirectionFilter'
      responses:
        '200':
          $ref: '#/components/responses/PageObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /customer/account-reports:
    get:
      tags: [Customer REST]
      summary: List account-level summaries derived from visible active leads.
      security:
        - CustomerApiKey: [leads:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/CompanyNameFilter'
        - $ref: '#/components/parameters/MinIcpMatchPercentFilter'
        - $ref: '#/components/parameters/MaxIcpMatchPercentFilter'
        - $ref: '#/components/parameters/DateFromFilter'
        - $ref: '#/components/parameters/DateToFilter'
        - $ref: '#/components/parameters/SearchQueryFilter'
        - $ref: '#/components/parameters/AccountSortFilter'
        - $ref: '#/components/parameters/SortDirectionFilter'
      responses:
        '200':
          $ref: '#/components/responses/PageObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
  /customer/account-reports/{accountId}:
    get:
      tags: [Customer REST]
      summary: Get one account summary with top visible leads.
      security:
        - CustomerApiKey: [leads:read]
      parameters:
        - $ref: '#/components/parameters/AccountIdPath'
        - $ref: '#/components/parameters/MinIcpMatchPercentFilter'
        - $ref: '#/components/parameters/MaxIcpMatchPercentFilter'
        - $ref: '#/components/parameters/DateFromFilter'
        - $ref: '#/components/parameters/DateToFilter'
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
        '404':
          $ref: '#/components/responses/NotFound'
  /customer/integrations:
    get:
      tags: [Customer REST]
      summary: Get safe integration status.
      security:
        - CustomerApiKey: [integrations:read]
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /customer/integrations/status:
    get:
      tags: [Customer REST]
      summary: Get integration status through the status alias.
      security:
        - CustomerApiKey: [integrations:read]
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /customer/integrations/leads/{leadId}/delivery:
    get:
      tags: [Customer REST]
      summary: Get lead delivery state.
      security:
        - CustomerApiKey: [integrations:read]
      parameters:
        - $ref: '#/components/parameters/LeadId'
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
  /customer/integrations/lead-delivery-state:
    get:
      tags: [Customer REST]
      summary: Get lead delivery state by query.
      security:
        - CustomerApiKey: [integrations:read]
      parameters:
        - name: leadId
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
  /customer/sync/status:
    get:
      tags: [Customer REST]
      summary: Get sync status.
      security:
        - CustomerApiKey: [sync:read]
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /customer/qualification/runs/latest:
    get:
      tags: [Customer REST]
      summary: Get latest qualification run.
      security:
        - CustomerApiKey: [sync:read]
      responses:
        '200':
          $ref: '#/components/responses/DataObject'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /audience-analytics/health:
    get:
      tags: [Ordinal Audience Analytics]
      summary: Check Audience Analytics readiness for the key workspace.
      security:
        - CustomerApiKey: [audience_analytics:read]
      responses:
        '200':
          $ref: '#/components/responses/AudienceObject'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
  /audience-analytics/linkedin-posts:
    get:
      tags: [Ordinal Audience Analytics]
      summary: Get aggregate-only audience analytics for a LinkedIn post.
      security:
        - CustomerApiKey: [audience_analytics:read]
      parameters:
        - name: postUrn
          in: query
          required: true
          description: LinkedIn post URN (`urn:li:activity:...`, `urn:li:share:...`, `urn:li:ugcPost:...`) or a LinkedIn `feed/update` URL containing one of those URNs.
          schema:
            type: string
            maxLength: 1024
          examples:
            activityUrn:
              value: urn:li:activity:123456789
            feedUpdateUrl:
              value: https://www.linkedin.com/feed/update/urn:li:share:123456789/
      responses:
        '200':
          $ref: '#/components/responses/AudienceObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/UnresolvedUrn'
  /audience-analytics/linkedin-posts/find-audience:
    post:
      tags: [Ordinal Audience Analytics]
      summary: Request on-demand audience discovery for one exact LinkedIn post.
      description: Resolves the supplied LinkedIn reference to one immutable post, prepares an exact qualification quote, and accepts it as a pipeline-capacity request. Replays with the same idempotency key and payload reuse the durable request. An incompatible replay returns 409.
      security:
        - CustomerApiKey: [audience_analytics:request]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [postUrn]
              additionalProperties: false
              properties:
                postUrn:
                  type: string
                  maxLength: 1024
                  description: LinkedIn post URN or `feed/update` URL containing a LinkedIn post URN.
                idempotencyKey:
                  type: string
                  maxLength: 120
      responses:
        '202':
          description: Discovery is already available, waiting for quote preparation, or represented by a durable qualification request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AudienceDiscoveryResponse'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: The full exact quote could not be reserved. No qualification request was created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/UnresolvedUrn'
        '429':
          $ref: '#/components/responses/RateLimited'
  /audience-analytics/organization:
    get:
      tags: [Ordinal Audience Analytics]
      summary: Get aggregate audience breakdown for the key workspace.
      security:
        - CustomerApiKey: [audience_analytics:read]
      parameters:
        - $ref: '#/components/parameters/Range'
        - name: dimension
          in: query
          schema:
            type: string
            enum: [title, companySize, location, industry]
            default: industry
      responses:
        '200':
          $ref: '#/components/responses/AudienceObject'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCapability'
  /mcp/oauth/register:
    post:
      tags: [MCP OAuth]
      summary: Register a public MCP OAuth client.
      description: Dynamic Client Registration for public clients. Redirect URIs must be safe loopback URIs or explicitly allowlisted HTTPS origins.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/McpOAuthClientRegistrationRequest'
      responses:
        '201':
          description: Registered public client metadata.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpOAuthClientRegistrationResponse'
        '400':
          $ref: '#/components/responses/ValidationFailed'
  /mcp/oauth/authorize:
    get:
      tags: [MCP OAuth]
      summary: Start MCP OAuth authorization.
      description: Validates client, redirect URI, resource, scopes, and PKCE challenge, then redirects to the traxy consent screen.
      security: []
      parameters:
        - name: response_type
          in: query
          required: true
          schema:
            type: string
            enum: [code]
        - name: client_id
          in: query
          required: true
          schema:
            type: string
        - name: redirect_uri
          in: query
          required: true
          schema:
            type: string
            format: uri
        - name: code_challenge
          in: query
          required: true
          schema:
            type: string
        - name: code_challenge_method
          in: query
          required: true
          schema:
            type: string
            enum: [S256]
        - name: scope
          in: query
          schema:
            type: string
        - name: resource
          in: query
          schema:
            type: string
            format: uri
        - name: state
          in: query
          schema:
            type: string
      responses:
        '302':
          description: Redirect to traxy consent screen.
        '400':
          $ref: '#/components/responses/ValidationFailed'
  /mcp/oauth/authorize/request:
    get:
      tags: [MCP OAuth]
      summary: Preview a pending MCP OAuth authorization request.
      security:
        - SupabaseUserJwt: []
      parameters:
        - name: request
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Safe consent-screen metadata.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpOAuthAuthorizationPreview'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /mcp/oauth/authorize/permissions:
    post:
      tags: [MCP OAuth]
      summary: Preview current account permissions in selected workspaces.
      description: Read-only authenticated preview. The request token stays in the body. Each workspace must pass current membership and MCP availability checks.
      security:
        - SupabaseUserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/McpOAuthAuthorizationPermissionsRequest'
      responses:
        '200':
          description: Available scope ceiling and per-workspace scopes. Response is not cacheable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpOAuthAuthorizationPermissions'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/InsufficientCapability'
  /mcp/oauth/authorize/complete:
    post:
      tags: [MCP OAuth]
      summary: Approve or cancel MCP OAuth authorization from traxy.
      description: Every active member may approve the requested actions permitted by their current role and MCP entitlement in the selected workspaces. Submitted scopes cap consent; each tool call also enforces the current role. Cancellation returns an access_denied callback.
      security:
        - SupabaseUserJwt: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/McpOAuthAuthorizeCompleteRequest'
      responses:
        '200':
          description: Redirect URL for the MCP client callback.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpOAuthAuthorizeCompleteResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/InsufficientCapability'
  /mcp/oauth/token:
    post:
      tags: [MCP OAuth]
      summary: Exchange an authorization code or refresh token.
      description: Supports authorization_code with PKCE S256 and refresh_token with rotation.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/McpOAuthTokenRequest'
      responses:
        '200':
          description: MCP OAuth token response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpOAuthTokenResponse'
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/McpUnauthorized'
  /mcp/oauth/revoke:
    post:
      tags: [MCP OAuth]
      summary: Revoke an MCP OAuth token.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/McpOAuthRevokeRequest'
      responses:
        '200':
          description: Token revoke accepted.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
  /mcp:
    get:
      tags: [MCP]
      summary: MCP GET sessions are not enabled in V1.
      security:
        - McpOAuth: []
      responses:
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
    post:
      tags: [MCP]
      summary: Streamable HTTP JSON-RPC endpoint for remote MCP clients.
      security:
        - McpOAuth: []
      parameters:
        - name: Accept
          in: header
          required: true
          schema:
            type: string
            example: 'application/json, text/event-stream'
        - name: MCP-Protocol-Version
          in: header
          required: false
          schema:
            type: string
            example: '2025-11-25'
          description: Use the version negotiated during initialize (2025-11-25 or 2025-06-18). The header may be omitted until negotiation completes.
        - name: Origin
          in: header
          schema:
            type: string
          description: Requests from a browser origin must come from an origin allowed by traxy. Non-browser clients can omit this header.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/McpJsonRpcRequest'
      responses:
        '200':
          description: JSON-RPC response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/McpJsonRpcResponse'
              examples:
                toolsListRepresentative:
                  summary: Representative tools/list response with current MCP annotations.
                  value:
                    jsonrpc: '2.0'
                    id: 2
                    result:
                      tools:
                        - name: traxy_get_workspace_access
                          title: Get workspace access
                          description: Return visible workspace access, usage, and capabilities.
                          inputSchema:
                            type: object
                            properties: {}
                            additionalProperties: false
                          annotations:
                            readOnlyHint: true
                          outputSchema:
                            type: object
                            additionalProperties: true
                        - name: traxy_get_mcp_connection_status
                          title: Get MCP connection status
                          description: Return safe diagnostics for the authenticated MCP connection.
                          inputSchema:
                            type: object
                            properties: {}
                            additionalProperties: false
                          annotations:
                            readOnlyHint: true
                          outputSchema:
                            type: object
                            additionalProperties: true
                        - name: traxy_get_agent_status
                          title: Get agent status
                          description: Return safe read-only status for the automated lead-finding agent.
                          inputSchema:
                            type: object
                            properties: {}
                            additionalProperties: false
                          annotations:
                            readOnlyHint: true
                          outputSchema:
                            type: object
                            additionalProperties: true
                        - name: traxy_list_agent_candidates
                          title: List agent candidates
                          description: List safe visible candidates for one agent run, the latest agent run, or the latest preview with locked-count availability.
                          inputSchema:
                            type: object
                            properties:
                              runId:
                                type: string
                                format: uuid
                              source:
                                type: string
                                enum:
                                  [latest_agent_run, latest_onboarding_preview]
                              limit:
                                type: number
                                minimum: 1
                                maximum: 100
                              offset:
                                type: number
                                minimum: 0
                                maximum: 10000
                            additionalProperties: false
                          annotations:
                            readOnlyHint: true
                          outputSchema:
                            type: object
                            additionalProperties: true
                toolArgumentValidationError:
                  summary: Known, authorized tool with model-correctable invalid arguments.
                  value:
                    jsonrpc: '2.0'
                    id: call-1
                    result:
                      content:
                        - type: text
                          text: '{"error":{"code":"validation_failed","message":"Tool traxy_get_lead failed input validation. Fix the arguments and retry.","issues":[{"path":"arguments.leadId","code":"invalid_string","message":"Invalid uuid"}]}}'
                      structuredContent:
                        error:
                          code: validation_failed
                          message: Tool traxy_get_lead failed input validation. Fix the arguments and retry.
                          issues:
                            - path: arguments.leadId
                              code: invalid_string
                              message: Invalid uuid
                      isError: true
        '202':
          description: Notification accepted. The response has no body.
        '400':
          $ref: '#/components/responses/McpBadRequest'
        '401':
          $ref: '#/components/responses/McpUnauthorized'
        '403':
          $ref: '#/components/responses/McpOriginNotAllowed'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  securitySchemes:
    CustomerApiKey:
      type: http
      scheme: bearer
      bearerFormat: trxy_live_<public_id>_<secret>
      description: API key created from Integrations > API & MCP > API keys. Used for Customer REST and Ordinal Audience Analytics, not MCP OAuth.
    McpOAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Short-lived OAuth access token issued by traxy for the MCP resource.
    SupabaseUserJwt:
      type: http
      scheme: bearer
      bearerFormat: Supabase JWT
      description: Authenticated traxy app session token used by the consent screen.
  parameters:
    Limit:
      name: limit
      in: query
      description: Number of items to return. The default is 50 and the maximum is 100.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 50
    Cursor:
      name: cursor
      in: query
      description: Opaque cursor returned in `page.nextCursor`. Pass it unchanged with the same filters and sort. Incompatible cursors return `validation_failed`.
      schema:
        type: string
        maxLength: 256
    LeadIdFilter:
      name: leadId
      in: query
      schema:
        type: string
        format: uuid
    LeadTierFilter:
      name: tier
      in: query
      schema:
        type: string
        enum: [UNQUALIFIED, NURTURE, QUALIFIED, PRIORITY]
    LeadTiersFilter:
      name: leadTiers
      in: query
      description: Comma-separated lead tiers. Accepts up to four values.
      schema:
        type: string
    LeadDispositionFilter:
      name: disposition
      in: query
      schema:
        type: string
        enum: [ACTIVE, CLIENT, BAD_LEAD]
    LeadDispositionsFilter:
      name: dispositions
      in: query
      description: Comma-separated dispositions. Accepts up to three values. Omitted dispositions default to ACTIVE.
      schema:
        type: string
    MinIcpMatchPercentFilter:
      name: minIcpMatchPercent
      in: query
      schema:
        type: integer
        minimum: 0
        maximum: 100
    MaxIcpMatchPercentFilter:
      name: maxIcpMatchPercent
      in: query
      schema:
        type: integer
        minimum: 0
        maximum: 100
    MonitoredProfileIdFilter:
      name: monitoredProfileId
      in: query
      schema:
        type: string
        format: uuid
    MonitoredProfileIdsFilter:
      name: monitoredProfileIds
      in: query
      description: Comma-separated monitored profile IDs. Accepts up to 50 IDs.
      schema:
        type: string
    PostIdFilter:
      name: postId
      in: query
      schema:
        type: string
        format: uuid
    EngagementTypeFilter:
      name: engagementType
      in: query
      schema:
        type: string
        enum: [LIKE, COMMENT, REPOST, POST_AUTHOR]
    EngagementTypesFilter:
      name: engagementTypes
      in: query
      description: Comma-separated engagement types. Accepts up to 4 values.
      schema:
        type: string
    OwnerIdFilter:
      name: ownerId
      in: query
      schema:
        type: string
        format: uuid
    OwnerIdsFilter:
      name: ownerIds
      in: query
      description: Comma-separated owner IDs. Accepts up to 50 IDs.
      schema:
        type: string
    DateFromFilter:
      name: dateFrom
      in: query
      description: Inclusive start of the date range.
      schema:
        type: string
        format: date-time
    DateToFilter:
      name: dateTo
      in: query
      description: Inclusive end of the date range.
      schema:
        type: string
        format: date-time
    SearchQueryFilter:
      name: query
      in: query
      schema:
        type: string
        minLength: 1
        maxLength: 160
    CompanyNameFilter:
      name: companyName
      in: query
      schema:
        type: string
        minLength: 1
        maxLength: 160
    CompanyNamesFilter:
      name: companyNames
      in: query
      description: Comma-separated company names. Accepts up to 25 values.
      schema:
        type: string
    LeadOriginFilter:
      name: origin
      in: query
      description: Safe lead origin. `agent` includes automated Agent and preview discovery; `watchlist` includes watched-profile and manual workflows.
      schema:
        type: string
        enum: [agent, watchlist, all]
        default: all
    ProfileTypeFilter:
      name: profileType
      in: query
      schema:
        type: string
        enum: [TEAMMATE, COMPETITOR, INFLUENCER, INVESTOR, CUSTOM, TARGET_ACCOUNT, CUSTOMER, PARTNER]
    ProfileTypesFilter:
      name: profileTypes
      in: query
      description: Comma-separated profile types. Accepts up to five values.
      schema:
        type: string
    MonitoredProfileSourceFilter:
      name: source
      in: query
      schema:
        type: string
        enum: [MEMBER_ONBOARDING, MANUAL_WATCHLIST]
    SlotKindFilter:
      name: slotKind
      in: query
      schema:
        type: string
        enum: [SELF, STANDARD, COMPETITOR]
    RelationshipLabelFilter:
      name: relationshipLabel
      in: query
      schema:
        type: string
        minLength: 1
        maxLength: 80
    WatchlistStateFilter:
      name: state
      in: query
      schema:
        type: string
        enum: [ACTIVE, PAUSED]
    WatchlistSortFilter:
      name: sort
      in: query
      schema:
        type: string
        enum: [createdAt, followers, lastEngagementSyncAt, displayName]
        default: createdAt
    ContentStatusFilter:
      name: status
      in: query
      schema:
        type: string
        enum: [any, qualified, unqualified]
        default: any
    ContentSortFilter:
      name: sort
      in: query
      schema:
        type: string
        enum: [date, engagement, reactions, comments, reposts]
        default: date
    AccountSortFilter:
      name: sort
      in: query
      schema:
        type: string
        enum: [latestSignalAt, leadCount, averageIcpMatchPercent, accountName]
        default: latestSignalAt
    SortDirectionFilter:
      name: sortDirection
      in: query
      schema:
        type: string
        enum: [asc, desc]
        default: desc
    Range:
      name: range
      in: query
      schema:
        type: string
        enum: [7d, 30d, 90d]
        default: 30d
    LeadId:
      name: leadId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    PostId:
      name: postId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    MonitoredProfileIdPath:
      name: monitoredProfileId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    AccountIdPath:
      name: accountId
      in: path
      required: true
      schema:
        type: string
        minLength: 1
        maxLength: 256
  responses:
    DataObject:
      description: Data envelope.
      content:
        application/json:
          schema:
            type: object
            additionalProperties: true
    PageObject:
      description: Paginated data envelope.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PageEnvelope'
    AudienceObject:
      description: Aggregate-only audience response.
      content:
        application/json:
          schema:
            type: object
            additionalProperties: true
    Unauthorized:
      description: Invalid, expired, or revoked API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    McpUnauthorized:
      description: Invalid, expired, revoked, or wrong-audience MCP OAuth token. Response includes a WWW-Authenticate challenge with resource metadata.
      headers:
        WWW-Authenticate:
          schema:
            type: string
            example: Bearer resource_metadata="https://api.traxy.ai/.well-known/oauth-protected-resource/api/v1/mcp" scope="workspace:read leads:read leads:contact_read agent:read"
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    InsufficientCapability:
      description: Key or OAuth grant lacks the required scope.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    ValidationFailed:
      description: Request failed boundary validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    McpBadRequest:
      description: MCP transport/header failure or top-level request validation failure.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/McpJsonRpcResponse'
              - $ref: '#/components/schemas/ErrorEnvelope'
    NotFound:
      description: Resource not found for the key workspace.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    UnresolvedUrn:
      description: Malformed or unsupported LinkedIn URN. Valid absent and cross-workspace post URNs use NotFound.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    McpOriginNotAllowed:
      description: Browser Origin is not allowed for MCP.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    MethodNotAllowed:
      description: HTTP method not supported for this endpoint.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    RateLimited:
      description: Customer API rate limit exceeded or rate-limit enforcement is temporarily unavailable. Retry after the delay in Retry-After.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
  schemas:
    CustomerMeetingsSummary:
      type: object
      properties:
        qualifiedCompanies:
          type: integer
          minimum: 0
          maximum: 9007199254740991
        attributedMeetings:
          type: integer
          minimum: 0
          maximum: 9007199254740991
        unmatchedBookings:
          type: integer
          minimum: 0
          maximum: 9007199254740991
        range:
          default: 30d
          type: string
          enum:
            - 7d
            - 30d
            - 90d
            - all
        lastAttributedAt:
          type: string
          format: date-time
          pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
          nullable: true
        windows:
          type: object
          properties:
            7d:
              type: object
              properties:
                qualifiedCompanies:
                  type: integer
                  minimum: 0
                  maximum: 9007199254740991
                attributedMeetings:
                  type: integer
                  minimum: 0
                  maximum: 9007199254740991
                unmatchedBookings:
                  type: integer
                  minimum: 0
                  maximum: 9007199254740991
              required:
                - qualifiedCompanies
                - attributedMeetings
                - unmatchedBookings
              additionalProperties: false
            30d:
              type: object
              properties:
                qualifiedCompanies:
                  type: integer
                  minimum: 0
                  maximum: 9007199254740991
                attributedMeetings:
                  type: integer
                  minimum: 0
                  maximum: 9007199254740991
                unmatchedBookings:
                  type: integer
                  minimum: 0
                  maximum: 9007199254740991
              required:
                - qualifiedCompanies
                - attributedMeetings
                - unmatchedBookings
              additionalProperties: false
          required:
            - 7d
            - 30d
          additionalProperties: false
      required:
        - qualifiedCompanies
        - attributedMeetings
        - unmatchedBookings
        - range
        - lastAttributedAt
      additionalProperties: false
    CustomerMeetingsFeed:
      type: object
      properties:
        data:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                format: uuid
                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
              meetingCount:
                type: integer
                minimum: 0
                maximum: 9007199254740991
              targetType:
                type: string
                enum:
                  - PERSON
                  - COMPANY
              lifecycleStatus:
                type: string
                enum:
                  - PENDING
                  - TENTATIVE
                  - ACCEPTED
                  - DECLINED
                  - OCCURRED
                  - CANCELLED
              direction:
                type: string
                enum:
                  - INBOUND
                  - OUTBOUND
                  - UNKNOWN
              meetingStartAt:
                type: string
                format: date-time
                pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
              bookedAt:
                type: string
                format: date-time
                pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
              attributedAt:
                type: string
                format: date-time
                pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
              company:
                type: object
                properties:
                  name:
                    type: string
                    nullable: true
                  domain:
                    type: string
                    nullable: true
                required:
                  - name
                  - domain
                additionalProperties: false
              leadRelationship:
                type: string
                enum:
                  - DIRECT
                  - COMPANY
                nullable: true
              meetingOutcome:
                type: string
                enum:
                  - already_client
                  - closed_won
                nullable: true
              lead:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  name:
                    type: string
                  headline:
                    type: string
                    nullable: true
                  title:
                    type: string
                    nullable: true
                  icpMatchPercent:
                    type: number
                  foundAt:
                    type: string
                    nullable: true
                required:
                  - id
                  - name
                  - headline
                  - title
                  - icpMatchPercent
                  - foundAt
                additionalProperties: false
                nullable: true
              meetingWith:
                type: object
                properties:
                  name:
                    type: string
                    nullable: true
                  email:
                    type: string
                    nullable: true
                required:
                  - name
                  - email
                additionalProperties: false
                nullable: true
            required:
              - id
              - meetingCount
              - targetType
              - lifecycleStatus
              - direction
              - meetingStartAt
              - bookedAt
              - attributedAt
              - company
              - leadRelationship
              - meetingOutcome
              - lead
            additionalProperties: false
        page:
          type: object
          properties:
            limit:
              type: integer
              minimum: 1
              maximum: 100
            cursor:
              type: string
              nullable: true
            nextCursor:
              type: string
              nullable: true
          required:
            - limit
            - cursor
            - nextCursor
          additionalProperties: false
      required:
        - data
        - page
      additionalProperties: false
    McpOAuthClientRegistrationRequest:
      type: object
      additionalProperties: false
      required: [redirect_uris]
      properties:
        client_name:
          type: string
          example: Codex
        redirect_uris:
          type: array
          minItems: 1
          items:
            type: string
            format: uri
          example: ['http://localhost:1455/callback']
        grant_types:
          type: array
          items:
            type: string
            enum: [authorization_code, refresh_token]
        response_types:
          type: array
          items:
            type: string
            enum: [code]
        scope:
          type: string
          description: Space-separated OAuth scopes.
          example: 'workspace:read leads:read leads:contact_read agent:read'
        token_endpoint_auth_method:
          type: string
          enum: [none]
          default: none
    McpOAuthClientRegistrationResponse:
      type: object
      required:
        - client_id
        - client_name
        - redirect_uris
        - grant_types
        - response_types
        - token_endpoint_auth_method
        - scope
      properties:
        client_id:
          type: string
          example: mcp_abc123
        client_name:
          type: string
          example: Codex
        redirect_uris:
          type: array
          items:
            type: string
            format: uri
        grant_types:
          type: array
          items:
            type: string
        response_types:
          type: array
          items:
            type: string
        token_endpoint_auth_method:
          type: string
          enum: [none]
        scope:
          type: string
          example: 'workspace:read leads:read leads:contact_read analytics:read agent:read'
    McpOAuthAuthorizationPreview:
      type: object
      required: [clientName, requestedScopes, expiresAt, resource]
      properties:
        clientName:
          type: string
          example: Codex
        requestedScopes:
          $ref: '#/components/schemas/McpConsentScopes'
        expiresAt:
          type: string
          format: date-time
        resource:
          type: string
          format: uri
          example: https://api.traxy.ai/api/v1/mcp
    McpConsentScopes:
      type: array
      maxItems: 20
      items:
        type: string
        enum:
          - leads:read
          - leads:contact_read
          - leads:contact_enrich
          - leads:manage
          - analytics:read
          - icp:read
          - icp:manage
          - watchlist:read
          - watchlist:manage
          - content:read
          - integrations:read
          - integrations:manage
          - integrations:deliver
          - sync:read
          - agent:read
          - agent:run
          - workspace:read
          - audience_analytics:read
          - audience_analytics:request
          - post_qualification:manage
    McpOAuthAuthorizationPermissionsRequest:
      type: object
      additionalProperties: false
      required: [requestToken, organizationIds]
      properties:
        requestToken:
          type: string
          minLength: 32
          maxLength: 256
        organizationIds:
          type: array
          minItems: 1
          maxItems: 50
          items:
            type: string
            format: uuid
    McpOAuthAuthorizationPermissions:
      type: object
      required: [scopes, workspaces]
      properties:
        scopes:
          $ref: '#/components/schemas/McpConsentScopes'
        workspaces:
          type: array
          minItems: 1
          maxItems: 50
          items:
            type: object
            required: [organizationId, scopes]
            properties:
              organizationId:
                type: string
                format: uuid
              scopes:
                $ref: '#/components/schemas/McpConsentScopes'
    McpOAuthAuthorizeCompleteRequest:
      oneOf:
        - type: object
          additionalProperties: false
          required: [requestToken, organizationId, approved]
          properties:
            requestToken:
              type: string
              minLength: 32
              maxLength: 256
            organizationId:
              type: string
              format: uuid
            organizationIds:
              type: array
              minItems: 1
              maxItems: 50
              items:
                type: string
                format: uuid
            scopes:
              allOf:
                - $ref: '#/components/schemas/McpConsentScopes'
                - minItems: 1
            approved:
              type: boolean
              enum: [true]
        - type: object
          additionalProperties: false
          required: [requestToken, approved]
          properties:
            requestToken:
              type: string
              minLength: 32
              maxLength: 256
            organizationId:
              type: string
              format: uuid
            approved:
              type: boolean
              enum: [false]
    McpOAuthAuthorizeCompleteResponse:
      type: object
      required: [redirectUrl]
      properties:
        redirectUrl:
          type: string
          format: uri
          description: Client callback URL. Do not log or send to analytics because it may contain an authorization code.
    McpOAuthTokenRequest:
      oneOf:
        - type: object
          additionalProperties: false
          required:
            - grant_type
            - code
            - redirect_uri
            - client_id
            - code_verifier
          properties:
            grant_type:
              type: string
              enum: [authorization_code]
            code:
              type: string
            redirect_uri:
              type: string
              format: uri
            client_id:
              type: string
            code_verifier:
              type: string
              minLength: 43
              maxLength: 128
            resource:
              type: string
              format: uri
        - type: object
          additionalProperties: false
          required: [grant_type, refresh_token, client_id]
          properties:
            grant_type:
              type: string
              enum: [refresh_token]
            refresh_token:
              type: string
            client_id:
              type: string
            scope:
              type: string
            resource:
              type: string
              format: uri
    McpOAuthTokenResponse:
      type: object
      required: [access_token, token_type, expires_in, refresh_token, scope]
      properties:
        access_token:
          type: string
          description: Short-lived JWT for `/api/v1/mcp`.
        token_type:
          type: string
          enum: [Bearer]
        expires_in:
          type: integer
          example: 900
        refresh_token:
          type: string
          description: Opaque refresh token. Store securely.
        scope:
          type: string
          example: 'workspace:read leads:read leads:contact_read analytics:read agent:read'
    McpOAuthRevokeRequest:
      type: object
      additionalProperties: false
      required: [token]
      properties:
        token:
          type: string
        token_type_hint:
          type: string
          enum: [access_token, refresh_token]
    ErrorEnvelope:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, status, request_id]
          properties:
            code:
              type: string
              example: invalid_api_key
            message:
              type: string
              example: Invalid API key.
            status:
              type: integer
              example: 401
            request_id:
              type: string
    WorkspaceAccess:
      type: object
      required:
        - organizationId
        - dataAccess
        - usage
      properties:
        organizationId:
          type: string
          format: uuid
        dataAccess:
          type: object
          additionalProperties: false
          required:
            - workspace
            - leads
            - leadContactData
            - leadContactEnrichment
            - analytics
            - icp
            - watchlist
            - content
            - integrations
            - sync
            - agent
            - audienceAnalytics
            - audienceDiscovery
          properties:
            workspace:
              type: boolean
            leads:
              type: boolean
            leadContactData:
              type: boolean
            leadContactEnrichment:
              type: boolean
              description: Whether the credential can request contact enrichment.
            analytics:
              type: boolean
            icp:
              type: boolean
            watchlist:
              type: boolean
            content:
              type: boolean
            integrations:
              type: boolean
            sync:
              type: boolean
            agent:
              type: boolean
            audienceAnalytics:
              type: boolean
            audienceDiscovery:
              type: boolean
        usage:
          type: object
          additionalProperties: false
          required:
            - apiVisibleLeadCount
            - monitoredProfileCount
          properties:
            apiVisibleLeadCount:
              type: integer
            monitoredProfileCount:
              type: integer
    AudienceDiscoveryResponse:
      type: object
      additionalProperties: false
      required:
        - status
        - postId
      properties:
        status:
          type: string
          enum:
            - queued
            - pending
            - already_available
        postId:
          type: string
          format: uuid
        quoteId:
          type: string
          format: uuid
          nullable: true
          description: Exact quote used for the request, or the quote still being prepared.
        requestId:
          type: string
          format: uuid
          nullable: true
          description: Durable qualification request. Null only while its quote is still preparing.
        audience:
          type: object
          additionalProperties: true
          description: Aggregate-only audience data, present when analytics are already available.
    LeadPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelope'
        - type: object
          properties:
            data:
              type: array
              items:
                $ref: '#/components/schemas/Lead'
    LeadSignalPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelope'
        - type: object
          properties:
            data:
              type: array
              items:
                $ref: '#/components/schemas/LeadSignalRecord'
    LeadSignalRecord:
      type: object
      required:
        - id
        - engagementType
        - reactionType
        - engagedAt
        - capturedAt
        - signal
        - lead
        - primarySignal
        - engagedWith
        - post
      properties:
        id:
          type: string
          format: uuid
        engagementType:
          type: string
          enum: [LIKE, COMMENT, REPOST, POST_AUTHOR]
        reactionType:
          type: string
          nullable: true
        engagedAt:
          type: string
          format: date-time
        capturedAt:
          type: string
          format: date-time
        signal:
          type: object
          nullable: true
          allOf:
            - $ref: '#/components/schemas/LeadSignal'
        lead:
          $ref: '#/components/schemas/Lead'
        primarySignal:
          type: object
          additionalProperties: true
          description: Backward-compatible LinkedIn engagement context.
        engagedWith:
          type: object
          additionalProperties: true
        post:
          type: object
          additionalProperties: true
    LeadSignal:
      type: object
      additionalProperties: false
      required:
        - summary
        - whyRelevant
        - topic
        - type
        - strength
        - sentiment
        - occurredAt
        - source
      properties:
        summary:
          type: string
          maxLength: 300
        whyRelevant:
          type: string
          maxLength: 500
        topic:
          type: string
          nullable: true
          maxLength: 160
        type:
          type: string
          enum:
            [
              COMPETITOR,
              PAIN,
              TRIGGER,
              TOPIC,
              CATEGORY_INTEREST,
              CUSTOM,
              GENERAL_ENGAGEMENT,
            ]
        strength:
          type: string
          enum: [DIRECT, CONTEXTUAL, ACTIVITY]
        sentiment:
          type: string
          enum: [NEGATIVE, POSITIVE, MIXED, NEUTRAL, UNKNOWN]
        occurredAt:
          type: string
          format: date-time
        source:
          type: object
          additionalProperties: false
          required: [name, title, url]
          properties:
            name:
              type: string
              nullable: true
            title:
              type: string
              nullable: true
            url:
              type: string
              nullable: true
    Lead:
      type: object
      required:
        - id
        - profile
        - contact
        - signal
      properties:
        id:
          type: string
          format: uuid
        icpMatchPercent:
          type: integer
          nullable: true
        intentScore:
          type: number
        leadTier:
          type: string
          nullable: true
        profile:
          type: object
          additionalProperties: false
          required:
            - displayName
            - headline
            - location
            - currentCompany
            - profileUrl
          properties:
            displayName:
              type: string
              nullable: true
            headline:
              type: string
              nullable: true
            location:
              type: string
              nullable: true
            currentCompany:
              type: string
              nullable: true
            profileUrl:
              type: string
              format: uri
              nullable: true
              description: Canonical LinkedIn profile URL; null without leads:contact_read.
        contact:
          nullable: true
          description: Null unless the credential has leads:contact_read.
          type: object
          additionalProperties: false
          required:
            - workEmail
            - workEmailStatus
            - personalEmail
            - personalEmailStatus
            - contactPhone
            - contactPhoneStatus
          properties:
            workEmail:
              type: string
              format: email
              nullable: true
            workEmailStatus:
              type: string
              nullable: true
            personalEmail:
              type: string
              format: email
              nullable: true
            personalEmailStatus:
              type: string
              nullable: true
            contactPhone:
              type: string
              nullable: true
            contactPhoneStatus:
              type: string
              nullable: true
        signal:
          type: object
          nullable: true
          description: The lead's strongest current canonical signal, or null when unavailable.
          allOf:
            - $ref: '#/components/schemas/LeadSignal'
    PageEnvelope:
      type: object
      required: [data, page]
      properties:
        data:
          type: array
          items:
            type: object
            additionalProperties: true
        page:
          type: object
          required: [limit, nextCursor]
          properties:
            limit:
              type: integer
            cursor:
              type: string
              nullable: true
            nextCursor:
              type: string
              nullable: true
    McpJsonRpcRequest:
      type: object
      required: [jsonrpc, method]
      additionalProperties: false
      properties:
        jsonrpc:
          type: string
          enum: ['2.0']
        id:
          oneOf:
            - type: string
              nullable: true
            - type: number
        method:
          type: string
          description: MCP request or notification method, including initialize, tools/list, tools/call, and notifications/initialized.
        params:
          type: object
          additionalProperties: true
    McpJsonRpcResponse:
      type: object
      required: [jsonrpc]
      properties:
        jsonrpc:
          type: string
          enum: ['2.0']
        id:
          oneOf:
            - type: string
              nullable: true
            - type: number
        result:
          type: object
          additionalProperties: true
        error:
          type: object
          additionalProperties: true
