> ## Documentation Index
> Fetch the complete documentation index at: https://developers.mypilotindex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Read one Workforce screen

> One Workforce screen, read as the industry audience, with the filters a member's screen takes: at most three, a state only with its country, each a recorded value; an unknown filter, a repeated one or a value outside the vocabulary is 400 invalid_request. See the Insights tag for the floors. A publication that has never run answers only schema_version, screen, generated_at and cohort, with status not_ready. A screen MyPilotIndex has switched off is 400 invalid_request, as it is for a member. The token must carry insights.read, otherwise 403 scope_missing. A name outside the six screens is 404 not_found.



## OpenAPI

````yaml /api-reference/partner.yaml get /v1/partner/insights/workforce/{screen}
openapi: 3.1.0
info:
  title: MyPilotIndex Partner API
  version: 1.0.0
  description: >-
    The API of approved partner applications, such as logbooks, that act for a
    pilot who has granted them access on the MyPilotIndex consent screen. It is
    a separate document from the member API (openapi.yaml): partner routes live
    under /v1/partner/ and are never served to browsers. Authenticate with an
    access token from the identity provider's token endpoint
    (grant_type=client_credentials, scopes partner/<scope>) in the Authorization
    header. Every authenticated answer carries X-RateLimit-Limit and
    X-RateLimit-Remaining, the partner's daily allowance and what is left of it
    (a 401, and a path or method outside this document, carry neither); past the
    allowance the API answers 429 rate_limited with Retry-After. Errors use the
    shared envelope below. The API is additive within v1: new fields and routes
    may appear, existing ones do not change meaning.
servers:
  - url: https://{devApiHost}
    description: >-
      Sandbox on the AWS dev environment, with synthetic pilots. The host is
      issued to the partner with its sandbox credential.
    variables:
      devApiHost:
        default: dev-api.mypilotindex.com
        description: The dev API host given to the partner with the sandbox credential.
security: []
tags:
  - name: Grants
    description: >-
      A pilot connects on MyPilotIndex: the partner sends his browser to the
      consent page
      (https://mypilotindex.com/connect/{slug}?client_id&redirect_uri&scope&state&code_challenge&code_challenge_method=S256),
      the pilot signs in and allows, and the browser returns to the registered
      redirect_uri with code and state (or error=access_denied and state when he
      denies). The partner exchanges the code here for a grant and the pilot's
      pairwise id, which signs the pilot in ("Sign in with MyPilotIndex") and
      names him on every later call. A grant that is revoked, unknown or another
      partner's answers 404 on every route, identically.
  - name: Certificates
    description: >-
      Check a certificate a pilot shows you, such as his MPI Report, by its
      public id. The answer is the public verification's, and the call needs no
      grant.
  - name: Webhooks
    description: >-
      MyPilotIndex tells a partner about its grants by webhook: grant.revoked
      when a grant ends, and ping when the partner asks for a test. The webhook
      URL (https only) and its signing secret are set by MyPilotIndex when the
      partner is approved; the secret is shown once. Each event is a POST of the
      event as JSON (Content-Type application/json) signed in the Standard
      Webhooks scheme: webhook-id (the event id), webhook-timestamp and
      webhook-signature, "v1," and the base64 HMAC-SHA256 of
      "<webhook-id>.<webhook-timestamp>.<body>" under the key the secret carries
      in base64 after its whsec_ prefix, over the exact bytes received. Verify
      the signature, refuse a timestamp more than five minutes from your clock,
      dedupe on webhook-id, and answer any 2xx within 10 seconds. A 5xx, a 408,
      a 429, a redirect (never followed), a timeout or a connection failure is
      retried with growing gaps for about a day (twelve attempts), with the same
      webhook-id and a fresh timestamp and signature. A 4xx other than 408 and
      429 is taken as a refusal and not retried, except for a grant.revoked
      event with reason account_deleted: its grant leaves every list with the
      account, so it cannot be reconciled later, and it is retried within the
      same window on any answer but a 2xx, a 4xx included. A partner that missed
      other events reconciles with GET
      /v1/partner/grants?status=revoked&changed_since=.
  - name: Publishing
    description: >-
      Text a partner publishes: a post or a story as a pilot who granted
      feed.write or stories.write, and a post on the organization page the
      partner is bound to, with org.posts. Text only: no media, no YouTube link,
      no hangar, never anonymous. Every create takes a client_ref, 1 to 64 of
      letters, digits and . _ : - , unique per partner and kind: the same ref
      with the same body answers 200 with the stored id and replayed true,
      whatever the allowance says, so a retry after a lost answer is always
      safe; the same ref with another body, or on another grant, is 409
      client_ref_reused. Bodies are compared after normalizing, so surrounding
      spaces do not make two creates. Text may not carry an @handle or @all,
      because a mention notifies people: 422 mentions_not_allowed. A personal
      e-mail address in the text is 400 personal_contact; a company address is
      fine. Rolling allowances count what was published in the last 24 hours,
      removed or not, on top of the daily allowance of every call: 3 posts and 5
      stories per grant, 10 organization posts per partner; past them 429
      publication_throttled with Retry-After. A partner removes only what it
      created, and on a grant only what it created on that grant: anything else
      is the 404 of a row that does not exist, and so is a row the pilot already
      removed. A removal answers 204, and a create it removed still answers its
      id on a retry, with replayed true. A grant whose pilot may not publish
      right now, because his account is suspended or its deletion is pending,
      answers 404 on these routes as a revoked grant does, for as long as that
      lasts, though it stays active in GET /v1/partner/grants. A partner's posts
      and stories look like the pilot's own: no answer of the member API names
      the partner.
  - name: Jobs
    description: >-
      The active listings of MyPilotIndex, and the jobs of the company a partner
      is bound to. jobs.read and jobs.write are the partner's own scopes:
      MyPilotIndex allows them per partner, on the token alone, and no pilot
      sees them. A job goes live at once, without the review queue, as a
      company's job does: the partner is a company MyPilotIndex approved under a
      data agreement. Publishing needs the binding to a company, which
      MyPilotIndex sets; without one, 403 partner_not_bound. Every create takes
      a client_ref, 1 to 64 of letters, digits and . _ : - , unique per partner:
      the same ref with the same body answers 200 with the stored id and
      replayed true, whatever the allowance says, so a retry after a lost answer
      is always safe; the same ref with another body is 409 client_ref_reused.
      Bodies are compared after normalizing, so member order, surrounding spaces
      and the spelling of a number do not make two creates. A rolling allowance
      counts what was published in the last 24 hours, closed or not, on top of
      the daily allowance of every call: 50 jobs per partner; past it 429
      publication_throttled with Retry-After. A partner changes and closes only
      what it created, while the job's company is the one it is bound to:
      anything else is the 404 of a row that does not exist. Job text carries no
      mention rule and no contact guard: it does not reach the feed.
  - name: Insights
    description: >-
      The Workforce: what the pilots of MyPilotIndex look like as a market, in
      aggregates. insights.read is the partner's own scope, for the six screens;
      insights.position is a pilot's, for one pilot against his cohort. Every
      figure comes through the assembly the member screens use, so the floors
      are the server's: a cohort under the floor answers status insufficient and
      no section, a cell under its floor is suppressed, and a filter that would
      leave too few pilots is dropped, which filters.broadened says. No answer
      carries a count of pilots: a cohort is a band.
paths:
  /v1/partner/insights/workforce/{screen}:
    get:
      tags:
        - Insights
      summary: Read one Workforce screen
      description: >-
        One Workforce screen, read as the industry audience, with the filters a
        member's screen takes: at most three, a state only with its country,
        each a recorded value; an unknown filter, a repeated one or a value
        outside the vocabulary is 400 invalid_request. See the Insights tag for
        the floors. A publication that has never run answers only
        schema_version, screen, generated_at and cohort, with status not_ready.
        A screen MyPilotIndex has switched off is 400 invalid_request, as it is
        for a member. The token must carry insights.read, otherwise 403
        scope_missing. A name outside the six screens is 404 not_found.
      operationId: partners.read_workforce.v1
      parameters:
        - name: screen
          in: path
          required: true
          schema:
            type: string
            enum:
              - overview
              - geography
              - qualifications
              - aircraft
              - score
              - intent
        - $ref: '#/components/parameters/WorkforceCountry'
        - $ref: '#/components/parameters/WorkforceState'
        - $ref: '#/components/parameters/WorkforceSector'
        - $ref: '#/components/parameters/WorkforceRole'
        - $ref: '#/components/parameters/WorkforceHoursBand'
        - $ref: '#/components/parameters/WorkforceLicense'
        - $ref: '#/components/parameters/WorkforceAuthority'
        - $ref: '#/components/parameters/WorkforceAircraftCategory'
        - $ref: '#/components/parameters/WorkforceTypeRating'
        - $ref: '#/components/parameters/WorkforceScoreBand'
        - $ref: '#/components/parameters/WorkforceCareerTarget'
      responses:
        '200':
          description: The screen.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkforceScreen'
              example:
                schema_version: 1
                screen: overview
                generated_at: '2026-11-07T12:00:00Z'
                as_of: '2026-11-07'
                access: industry
                filters:
                  requested:
                    country: BR
                  applied:
                    country: BR
                  broadened: []
                cohort:
                  status: ok
                  n_band: 250_499
                  coverage:
                    hours: 0.9
                    licenses: 0.85
                    type_ratings: 0.4
                    score: 0.95
                    dob: 0.8
                privacy:
                  cell_floor: 5
                  cohort_floor: 20
                sections: {}
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          description: >-
            forbidden with reason scope_missing; the token does not carry
            insights.read.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScopeMissingError'
        '404':
          $ref: '#/components/responses/Error'
        '405':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/Error'
        '503':
          $ref: '#/components/responses/Error'
        '504':
          $ref: '#/components/responses/Error'
      security:
        - partnerOAuth:
            - partner/insights.read
components:
  parameters:
    WorkforceCountry:
      name: country
      in: query
      required: false
      description: An ISO 3166 alpha-2 country on record.
      schema:
        type: string
    WorkforceState:
      name: state
      in: query
      required: false
      description: A state on record in the country, which the read must also name.
      schema:
        type: string
    WorkforceSector:
      name: sector
      in: query
      required: false
      description: >-
        The sector of the pilot's current position, such as major_airline, cargo
        or corporate.
      schema:
        type: string
    WorkforceRole:
      name: role
      in: query
      required: false
      description: The pilot's role, such as captain, first_officer or instructor.
      schema:
        type: string
    WorkforceHoursBand:
      name: hours_band
      in: query
      required: false
      description: A band of total hours, such as 0_500 or 3001_5000.
      schema:
        type: string
    WorkforceLicense:
      name: license
      in: query
      required: false
      description: The pilot's highest license, such as atp or cpl.
      schema:
        type: string
    WorkforceAuthority:
      name: authority
      in: query
      required: false
      description: A licensing authority on record.
      schema:
        type: string
    WorkforceAircraftCategory:
      name: aircraft_category
      in: query
      required: false
      description: An aircraft category on record.
      schema:
        type: string
    WorkforceTypeRating:
      name: type_rating
      in: query
      required: false
      description: A type rating on record.
      schema:
        type: string
    WorkforceScoreBand:
      name: score_band
      in: query
      required: false
      description: A band of the MPI Score.
      schema:
        type: string
    WorkforceCareerTarget:
      name: career_target
      in: query
      required: false
      description: The career the pilot is aiming at.
      schema:
        type: string
  headers:
    RateLimitLimit:
      description: >-
        The partner's daily allowance of calls (1,000 unless agreed otherwise),
        per UTC day.
      schema:
        type: integer
    RateLimitRemaining:
      description: What is left of the allowance after this call.
      schema:
        type: integer
    RetryAfter:
      description: >-
        The HTTP date, in UTC, when the allowance renews (the next UTC
        midnight).
      schema:
        type: string
        example: Thu, 05 Nov 2026 00:00:00 GMT
  schemas:
    WorkforceScreen:
      type: object
      required:
        - schema_version
        - screen
        - generated_at
        - cohort
      description: >-
        One screen. The members past cohort are absent when the publication has
        never run. sections differs per screen: its blocks carry shares and
        bands, never a count of pilots, and a block that cannot clear its floor
        says so.
      properties:
        schema_version:
          type: integer
        screen:
          type: string
          enum:
            - overview
            - geography
            - qualifications
            - aircraft
            - score
            - intent
        generated_at:
          type: string
          format: date-time
        as_of:
          type:
            - string
            - 'null'
          format: date
          description: The newest recorded fact of the cohort.
        access:
          type: string
          enum:
            - industry
          description: The audience the answer was read for.
        filters:
          type: object
          additionalProperties: false
          required:
            - requested
            - applied
            - broadened
          properties:
            requested:
              $ref: '#/components/schemas/WorkforceFilters'
            applied:
              $ref: '#/components/schemas/WorkforceFilters'
            broadened:
              type: array
              description: The filters dropped to reach a cohort that clears the floor.
              items:
                type: string
        cohort:
          type: object
          required:
            - status
          properties:
            status:
              type: string
              enum:
                - ok
                - insufficient
                - not_ready
            n_band:
              type:
                - string
                - 'null'
              description: The cohort's size as a band. Only with status ok.
            coverage:
              type: object
              description: >-
                The fraction of the cohort that recorded each input. Only with
                status ok.
              additionalProperties:
                type: number
        privacy:
          $ref: '#/components/schemas/WorkforcePrivacy'
        sections:
          type: object
          additionalProperties: true
    ScopeMissingError:
      allOf:
        - $ref: '#/components/schemas/Error'
        - type: object
          properties:
            error:
              type: object
              properties:
                reason:
                  type: string
                  enum:
                    - scope_missing
    WorkforceFilters:
      type: object
      description: The filters of a read, each by its query name.
      additionalProperties:
        type: string
    WorkforcePrivacy:
      type: object
      additionalProperties: false
      required:
        - cell_floor
        - cohort_floor
      description: The floors the answer was measured against.
      properties:
        cell_floor:
          type: integer
        cohort_floor:
          type: integer
    Error:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          type: object
          additionalProperties: false
          required:
            - code
            - message
            - correlation_id
          properties:
            code:
              type: string
              enum:
                - invalid_request
                - request_too_large
                - unauthenticated
                - not_found
                - conflict
                - unprocessable
                - operation_disabled
                - rate_limited
                - forbidden
                - dependency_unavailable
                - internal
                - deadline_exceeded
                - method_not_allowed
            message:
              type: string
              description: Stable safe English message, never the underlying error text.
            correlation_id:
              type: string
              pattern: ^[a-f0-9]{32}$
            reason:
              type: string
              description: Optional refusal reason from the operation's closed vocabulary.
    RateLimitedError:
      allOf:
        - $ref: '#/components/schemas/Error'
        - type: object
          properties:
            error:
              type: object
              properties:
                reason:
                  type: string
                  enum:
                    - rate_limited
  responses:
    Error:
      description: The shared error envelope.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: >-
        rate_limited with reason rate_limited, past the partner's daily
        allowance; nothing was done.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RateLimitedError'
  securitySchemes:
    partnerOAuth:
      type: oauth2
      description: >-
        OAuth 2.0 client credentials. Request a token from the token endpoint of
        the identity provider's domain (https://{cognito-domain}/oauth2/token,
        with HTTP Basic client authentication) and send it as a Bearer token.
        Production uses auth.mypilotindex.com. The sandbox uses the dev
        environment's Cognito domain, which is given to the partner with its
        sandbox credential. A token carries only the scopes the partner was
        approved for.
      flows:
        clientCredentials:
          tokenUrl: https://auth.mypilotindex.com/oauth2/token
          scopes:
            partner/profile: Read the pilot's name, photo and username.
            partner/score: Read the pilot's MPI Score and phase.
            partner/hours.read: Read the pilot's flight hours.
            partner/hours.write: Update the pilot's flight hours with the logbook's totals.
            partner/licenses: Read the pilot's licenses, ratings and type ratings.
            partner/applications: Read the pilot's applications to jobs from the partner's company.
            partner/insights.position: Read how the pilot compares with his cohort.
            partner/jobs.match: Read the active jobs that fit the pilot best.
            partner/feed.write: Publish text posts as the pilot.
            partner/stories.write: Publish text stories as the pilot.
            partner/jobs.read: >-
              Read active job listings. MyPilotIndex allows it per partner,
              never a pilot.
            partner/jobs.write: >-
              Publish jobs for the partner's company. MyPilotIndex allows it per
              partner, never a pilot.
            partner/org.posts: >-
              Post on the partner's organization page. MyPilotIndex allows it
              per partner, never a pilot.
            partner/insights.read: >-
              Read Workforce screens. MyPilotIndex allows it per partner, never
              a pilot.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.