> ## 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 the active jobs that fit the pilot best

> The jobs that fit the pilot of a grant that holds jobs.match, drawn from the newest 50 active listings and ordered by the match, highest first, then by the newest. Each job carries the match as the pilot's own plan presents it, so a partner reads for a free pilot what he reads himself: the headline, with detail plan_required, and the breakdown and the missing requirements only for a paying pilot. match is null for a posting the engine does not score, such as a job for mechanics, and those come after the scored ones. A grant that is revoked, unknown or another partner's answers 404 not_found; a grant, or a token, without jobs.match is 403 scope_missing.



## OpenAPI

````yaml /api-reference/partner.yaml get /v1/partner/grants/{grant_id}/job-matches
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/grants/{grant_id}/job-matches:
    get:
      tags:
        - Grants
        - Jobs
      summary: Read the active jobs that fit the pilot best
      description: >-
        The jobs that fit the pilot of a grant that holds jobs.match, drawn from
        the newest 50 active listings and ordered by the match, highest first,
        then by the newest. Each job carries the match as the pilot's own plan
        presents it, so a partner reads for a free pilot what he reads himself:
        the headline, with detail plan_required, and the breakdown and the
        missing requirements only for a paying pilot. match is null for a
        posting the engine does not score, such as a job for mechanics, and
        those come after the scored ones. A grant that is revoked, unknown or
        another partner's answers 404 not_found; a grant, or a token, without
        jobs.match is 403 scope_missing.
      operationId: partners.pilot_job_matches.v1
      parameters:
        - name: grant_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 20
            default: 10
      responses:
        '200':
          description: The jobs, the best first.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartnerJobMatchPage'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          description: >-
            forbidden with reason scope_missing; the grant or the token does not
            hold jobs.match.
          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/jobs.match
components:
  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:
    PartnerJobMatchPage:
      type: object
      additionalProperties: false
      required:
        - items
      properties:
        items:
          type: array
          maxItems: 20
          items:
            $ref: '#/components/schemas/PartnerJobMatch'
    ScopeMissingError:
      allOf:
        - $ref: '#/components/schemas/Error'
        - type: object
          properties:
            error:
              type: object
              properties:
                reason:
                  type: string
                  enum:
                    - scope_missing
    PartnerJobMatch:
      allOf:
        - $ref: '#/components/schemas/PartnerJob'
        - type: object
          required:
            - match
          properties:
            match:
              oneOf:
                - $ref: '#/components/schemas/JobMatch'
                - type: 'null'
    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
    PartnerJob:
      type: object
      additionalProperties: false
      required:
        - id
        - title
        - company
        - position_type
        - employment_type
        - operation_type
        - base_location
        - base_airport_code
        - min_total_hours
        - min_pic_hours
        - min_multi_engine_hours
        - min_turbine_hours
        - min_jet_hours
        - required_licenses
        - required_type_ratings
        - salary_min
        - salary_max
        - salary_type
        - salary_currency
        - posted_at
        - expires_at
        - url
      description: >-
        A job. Nothing of who posted it, how it was reviewed or who applied
        travels with it.
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        company:
          $ref: '#/components/schemas/PartnerJobCompany'
        position_type:
          $ref: '#/components/schemas/JobPositionType'
        employment_type:
          $ref: '#/components/schemas/JobEmploymentType'
        operation_type:
          $ref: '#/components/schemas/JobOperationType'
        base_location:
          type:
            - string
            - 'null'
        base_airport_code:
          type:
            - string
            - 'null'
        min_total_hours:
          type:
            - integer
            - 'null'
        min_pic_hours:
          type:
            - integer
            - 'null'
        min_multi_engine_hours:
          type:
            - integer
            - 'null'
        min_turbine_hours:
          type:
            - integer
            - 'null'
        min_jet_hours:
          type:
            - integer
            - 'null'
        required_licenses:
          type: array
          items:
            type: string
        required_type_ratings:
          type: array
          items:
            type: string
        salary_min:
          type:
            - number
            - 'null'
        salary_max:
          type:
            - number
            - 'null'
        salary_type:
          type:
            - string
            - 'null'
          enum:
            - annual
            - hourly
            - per_diem
            - daily_rate
            - monthly
            - fixed
            - null
        salary_currency:
          type: string
          pattern: ^[A-Z]{3}$
        posted_at:
          type:
            - string
            - 'null'
          format: date-time
        expires_at:
          type:
            - string
            - 'null'
          format: date-time
        url:
          type: string
          description: The job's public page on MyPilotIndex.
    JobMatch:
      type: object
      required:
        - overall
        - matchLabel
        - engine_version
      description: >-
        A match as the pilot's plan presents it. A free pilot's carries overall,
        matchLabel, engine_version and detail plan_required; a paying pilot's
        replaces detail with breakdown and missingRequirements.
      properties:
        overall:
          type: number
          description: How well the job fits, from 0 to 100.
        matchLabel:
          type: string
          enum:
            - strong_match
            - good_match
            - partial_match
            - developing
        engine_version:
          type: string
        detail:
          type: string
          enum:
            - plan_required
        breakdown:
          type: object
          additionalProperties:
            type: number
        missingRequirements:
          type: array
          items:
            type: string
    PartnerJobCompany:
      type: object
      additionalProperties: false
      required:
        - id
        - name
        - logo_url
      description: >-
        Who the job is for. A job a member shared may name no company of the
        catalog; id and logo_url are then null.
      properties:
        id:
          type:
            - string
            - 'null'
          format: uuid
        name:
          type:
            - string
            - 'null'
        logo_url:
          type:
            - string
            - 'null'
    JobPositionType:
      type: string
      enum:
        - captain
        - first_officer
        - check_airman
        - instructor
        - management
        - other
        - program
    JobEmploymentType:
      type: string
      enum:
        - full_time
        - part_time
        - contract
        - temporary
        - seasonal
    JobOperationType:
      type: string
      enum:
        - part_121
        - part_135
        - part_91
        - part_91k
        - military
        - government
        - other
  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.