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

# Invite an adjuster to your firm

> Invites an adjuster to your firm by their OSAID. The adjuster sees the invitation in
the OSA app, with your firm's name and `invited_as`, and accepts or declines it.
Nothing about your claims reaches them until they accept. An invitation that isn't
answered expires after 7 days.

Once they accept, the adjuster can receive claims from you, and their name and email
appear in `GET /v1/adjusters`.

You can invite someone again after they declined, after the invitation expired, or
after you withdrew it. Inviting a current member fails with `409 already_member`, and
inviting someone whose invitation is still pending fails with `409 invitation_pending`.

An OSAID exists only once the adjuster has signed in to the OSA app. An OSAID that OSA
doesn't know fails with `422 unknown_osaid`.

**Your notes.** `invited_as`, `invited_by`, `purpose` and `note` are your firm's own
notes on the invitation. Nothing is checked against the account and no invitation is
refused because of them: whoever owns the OSAID can accept. The adjuster sees
`invited_as` on the invitation; the other three are for your firm only. Inviting someone
again replaces all four with whatever the new invitation sends.

**Test keys** can only invite your firm's test accounts. Any other OSAID fails with
`422 unknown_osaid`, exactly as if it didn't exist.

**Live keys** can't invite test accounts: a live key naming a test account fails with
`422 environment_mismatch`. Invite test accounts with a test key.




## OpenAPI

````yaml /openapi.yaml post /v1/adjusters/invitations
openapi: 3.1.0
info:
  title: OSA Partner API
  version: 1.0.0
  summary: Send claims to adjusters in the OSA app and follow their acceptance.
  description: >
    The OSA Partner API lets an adjusting firm's claims system invite adjusters
    to the firm,

    send them claims, and see whether each claim was accepted or rejected in the
    OSA app.


    **Conventions**

    - All requests and responses are JSON (`application/json`); errors use RFC
    9457 problem
      details (`application/problem+json`).
    - Field names are `snake_case`. Enum values are lowercase (for example
    `flood`,
      `pending_acceptance`), except US state and territory codes, which are the standard
      two-letter USPS codes, and flood zones, which are written as FEMA writes them (for
      example `AE`).
    - Enums are **open**: new values may be added to responses at any time, so
    your code must
      tolerate values it doesn't recognise.
    - Additive changes (new endpoints, new optional request fields, new response
    fields, new
      enum values) are made within `/v1`. Breaking changes go to a new version.
    - Requests are strict: an unknown or misspelled body field or query
    parameter is
      rejected with `422 validation_failed`, so typos can't be silently ignored.
    - Your own claim id (`external_id`) is stored on every claim and returned on
    every claim
      response, so you can match claims without keeping OSA's id.
    - Timestamps are RFC 3339 date-times in UTC. Dates that are calendar dates
    (such as the
      date of loss) are `YYYY-MM-DD` and are never converted between time zones.

    **Keys.** Every request except `GET /v1/health` needs an API key sent as

    `Authorization: Bearer <key>`. Live keys start with `osa_live_`; test keys
    start with

    `osa_test_` and only reach test claims and test accounts.
  contact:
    name: OSA API support
    email: info@osaconnection.com
    url: https://docs.osaconnection.com
servers:
  - url: https://api.osaconnection.com
    description: >-
      Production. Live and test keys both use this host; the key decides which
      data you reach.
security:
  - apiKey: []
tags:
  - name: Claims
    description: >
      Send claims to adjusters, offer them to another adjuster, and poll for
      acceptance.

      `/v1/claims` is always the claims your firm sent through the API.
  - name: Adjusters
    description: Invite adjusters to your firm by their OSAID and track their invitations.
  - name: Account
    description: Check which firm and permissions your API key carries.
  - name: Health
    description: Service availability. No key needed.
paths:
  /v1/adjusters/invitations:
    post:
      tags:
        - Adjusters
      summary: Invite an adjuster to your firm
      description: >
        Invites an adjuster to your firm by their OSAID. The adjuster sees the
        invitation in

        the OSA app, with your firm's name and `invited_as`, and accepts or
        declines it.

        Nothing about your claims reaches them until they accept. An invitation
        that isn't

        answered expires after 7 days.


        Once they accept, the adjuster can receive claims from you, and their
        name and email

        appear in `GET /v1/adjusters`.


        You can invite someone again after they declined, after the invitation
        expired, or

        after you withdrew it. Inviting a current member fails with `409
        already_member`, and

        inviting someone whose invitation is still pending fails with `409
        invitation_pending`.


        An OSAID exists only once the adjuster has signed in to the OSA app. An
        OSAID that OSA

        doesn't know fails with `422 unknown_osaid`.


        **Your notes.** `invited_as`, `invited_by`, `purpose` and `note` are
        your firm's own

        notes on the invitation. Nothing is checked against the account and no
        invitation is

        refused because of them: whoever owns the OSAID can accept. The adjuster
        sees

        `invited_as` on the invitation; the other three are for your firm only.
        Inviting someone

        again replaces all four with whatever the new invitation sends.


        **Test keys** can only invite your firm's test accounts. Any other OSAID
        fails with

        `422 unknown_osaid`, exactly as if it didn't exist.


        **Live keys** can't invite test accounts: a live key naming a test
        account fails with

        `422 environment_mismatch`. Invite test accounts with a test key.
      operationId: inviteAdjuster
      requestBody:
        required: true
        description: The adjuster to invite.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdjusterInvitationCreate'
            examples:
              invite:
                summary: Invite one adjuster
                value:
                  osa_id: Hp2sK8dL4fJ6gA1zX9cV3bN7mQ5w
              invite_with_notes:
                summary: Invite one adjuster, with your firm's notes
                value:
                  osa_id: Hp2sK8dL4fJ6gA1zX9cV3bN7mQ5w
                  invited_as: Todd Rivera
                  invited_by: Dana Whitfield, Claims Operations
                  purpose: Flood claims in the Tampa Bay area this season.
                  note: Referred by Jordan Ellis.
      responses:
        '201':
          description: The invitation was sent and is pending.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Adjuster'
              examples:
                pending:
                  summary: Invitation pending
                  value:
                    osa_id: Hp2sK8dL4fJ6gA1zX9cV3bN7mQ5w
                    status: pending
                    test: false
                    name: null
                    email: null
                    invited_at: '2026-09-28T13:02:45Z'
                    expires_at: '2026-10-05T13:02:45Z'
                    responded_at: null
                    invited_as: null
                    invited_by: null
                    purpose: null
                    note: null
                pending_with_notes:
                  summary: Invitation pending, sent with your firm's notes
                  value:
                    osa_id: Hp2sK8dL4fJ6gA1zX9cV3bN7mQ5w
                    status: pending
                    test: false
                    name: null
                    email: null
                    invited_at: '2026-09-28T13:02:45Z'
                    expires_at: '2026-10-05T13:02:45Z'
                    responded_at: null
                    invited_as: Todd Rivera
                    invited_by: Dana Whitfield, Claims Operations
                    purpose: Flood claims in the Tampa Bay area this season.
                    note: Referred by Jordan Ellis.
        '400':
          $ref: '#/components/responses/MalformedRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/InvitationConflict'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '422':
          $ref: '#/components/responses/InvitationUnprocessable'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
      security:
        - apiKey:
            - adjusters:write
components:
  schemas:
    AdjusterInvitationCreate:
      type: object
      description: >
        The adjuster to invite. `invited_as`, `invited_by`, `purpose` and `note`
        are your

        firm's own notes: nothing is checked against the account, and no
        invitation is refused

        because of them.
      additionalProperties: false
      required:
        - osa_id
      properties:
        osa_id:
          $ref: '#/components/schemas/OsaId'
          description: >-
            The OSAID of the adjuster to invite. The adjuster can read it in the
            OSA app.
        invited_as:
          type: string
          minLength: 1
          maxLength: 200
          description: >
            Who your firm believes it is inviting, for example the adjuster's
            name. Your firm's

            own note: nothing is checked against the account. It is shown to the
            adjuster on

            the invitation.
          examples:
            - Todd Rivera
        invited_by:
          type: string
          minLength: 1
          maxLength: 200
          description: >
            Who at your firm sent the invitation. Your firm's own note: nothing
            is checked

            against the account, and the adjuster doesn't see it.
        purpose:
          type: string
          minLength: 1
          maxLength: 500
          description: >
            What the invitation is for. Your firm's own note: nothing is checked
            against the

            account, and the adjuster doesn't see it.
        note:
          type: string
          minLength: 1
          maxLength: 1000
          description: >
            Anything else your firm wants to record with the invitation. Your
            firm's own note:

            nothing is checked against the account, and the adjuster doesn't see
            it.
    Adjuster:
      type: object
      description: An adjuster in your firm, or an invitation you sent.
      required:
        - osa_id
        - status
        - test
        - name
        - email
        - invited_at
        - expires_at
        - responded_at
        - invited_as
        - invited_by
        - purpose
        - note
      properties:
        osa_id:
          type: string
          description: The adjuster's OSAID.
        status:
          type: string
          description: >
            `pending`: invited, not yet answered. `active`: accepted and a
            member of your firm.

            `declined`: the adjuster declined. `expired`: not answered within 7
            days.

            `withdrawn`: you withdrew the invitation.
          enum:
            - pending
            - active
            - declined
            - expired
            - withdrawn
        test:
          type: boolean
          description: '`true` for one of your firm''s OSA-created test accounts.'
        name:
          type:
            - string
            - 'null'
          description: The adjuster's name; `null` until they accept.
        email:
          type:
            - string
            - 'null'
          description: The adjuster's email; `null` until they accept.
        invited_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            When the invitation was sent; `null` for members added without an
            invitation record.
        expires_at:
          type:
            - string
            - 'null'
          format: date-time
          description: When a pending invitation expires; `null` unless pending.
        responded_at:
          type:
            - string
            - 'null'
          format: date-time
          description: When the adjuster accepted or declined; `null` otherwise.
        invited_as:
          type:
            - string
            - 'null'
          description: >
            Who your firm said it was inviting; `null` when not sent. This is
            your firm's own

            note, so after acceptance it can differ from the account's real
            `name`.
        invited_by:
          type:
            - string
            - 'null'
          description: >-
            Who at your firm sent the invitation, as you sent it; `null` when
            not sent.
        purpose:
          type:
            - string
            - 'null'
          description: What the invitation is for, as you sent it; `null` when not sent.
        note:
          type:
            - string
            - 'null'
          description: >-
            Your firm's note on the invitation, as you sent it; `null` when not
            sent.
    OsaId:
      type: string
      description: >
        An adjuster's OSAID: their OSA account id. It exists once the adjuster
        has signed in to

        the OSA app, and they can read it from the app.
      minLength: 1
      maxLength: 128
      examples:
        - Xk3pQ9vT2mN8rL5wB7yC1dF4gH6j
    Problem:
      type: object
      description: >
        An RFC 9457 problem details object. Match on `code`, which is stable;
        `title` and

        `detail` are for people and may change. Quote `request_id` when
        contacting support.
      required:
        - type
        - title
        - status
        - detail
        - code
        - request_id
      properties:
        type:
          type: string
          format: uri
          description: A link to this error's documentation.
        title:
          type: string
          description: A short summary of the error.
        status:
          type: integer
          description: The HTTP status code.
        detail:
          type: string
          description: What went wrong in this request.
        code:
          type: string
          description: >
            The stable error code. `malformed_request` (400); `invalid_key`
            (401);

            `insufficient_scope`, `not_in_plan` (403); `not_found` (404);
            `duplicate_claim`,

            `claim_already_accepted`, `already_member`, `invitation_pending`
            (409);

            `payload_too_large` (413); `validation_failed`,
            `idempotency_key_reused`,

            `unknown_osaid`, `adjuster_not_in_firm`, `adjuster_not_accepting`,

            `environment_mismatch` (422); `rate_limited` (429); `internal_error`
            (500). New codes

            may be added.
          enum:
            - malformed_request
            - invalid_key
            - insufficient_scope
            - not_in_plan
            - not_found
            - duplicate_claim
            - claim_already_accepted
            - already_member
            - invitation_pending
            - payload_too_large
            - validation_failed
            - idempotency_key_reused
            - unknown_osaid
            - adjuster_not_in_firm
            - adjuster_not_accepting
            - environment_mismatch
            - rate_limited
            - internal_error
        request_id:
          type: string
          description: The id of this request. Quote it when contacting support.
        errors:
          type: array
          description: For `validation_failed`, each invalid field, parameter or header.
          items:
            type: object
            description: >-
              One problem. Exactly one of `pointer`, `parameter` or `header`
              says where.
            required:
              - detail
            properties:
              pointer:
                type: string
                description: >-
                  JSON Pointer to a body field, for example `/loss/date`. Empty
                  for the whole body.
              parameter:
                type: string
                description: The name of a query or path parameter, for example `limit`.
              header:
                type: string
                description: The name of a request header, for example `Idempotency-Key`.
              detail:
                type: string
                description: What is wrong with it.
        existing_claim_id:
          type: string
          format: uuid
          description: >-
            For `duplicate_claim`, the id of the claim that already exists.
            Always a claim your key can read.
        existing_claim_url:
          type: string
          format: uri
          description: >-
            For `duplicate_claim`, the API link to the claim that already
            exists. Always a claim your key can read.
  headers:
    RequestId:
      description: >
        The id of this request. Quote it to OSA support. On error responses it
        equals

        `request_id` in the body.
      required: true
      schema:
        type: string
      examples:
        request_id:
          summary: A request id
          value: 7f3e9a1c2b4d6e8f0a1b3c5d7e9f1a2b
    RetryAfter:
      description: Seconds to wait before retrying.
      required: true
      schema:
        type: integer
        minimum: 1
      examples:
        seconds:
          summary: Retry in 12 seconds
          value: 12
  responses:
    MalformedRequest:
      description: The body isn't valid JSON or can't be read (`malformed_request`).
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            malformed_request:
              summary: malformed_request
              value:
                type: https://docs.osaconnection.com/errors#malformed_request
                title: Malformed request
                status: 400
                detail: The request body isn't valid JSON.
                code: malformed_request
                request_id: 0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d
    Unauthorized:
      description: >
        The key is missing, malformed, unknown, revoked or expired
        (`invalid_key`).
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            invalid_key:
              summary: invalid_key
              value:
                type: https://docs.osaconnection.com/errors#invalid_key
                title: Invalid API key
                status: 401
                detail: The API key is missing, malformed, revoked or expired.
                code: invalid_key
                request_id: 7f3e9a1c2b4d6e8f0a1b3c5d7e9f1a2b
    Forbidden:
      description: >
        The key is valid but can't do this. `insufficient_scope`: the key's
        purpose doesn't

        include the permission this operation needs. `not_in_plan`: your firm's
        plan doesn't

        include it.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            insufficient_scope:
              summary: insufficient_scope
              value:
                type: https://docs.osaconnection.com/errors#insufficient_scope
                title: Insufficient scope
                status: 403
                detail: This key doesn't have the claims:write permission.
                code: insufficient_scope
                request_id: 0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e
            not_in_plan:
              summary: not_in_plan
              value:
                type: https://docs.osaconnection.com/errors#not_in_plan
                title: Not in plan
                status: 403
                detail: Your firm's plan doesn't include adjuster invitations.
                code: not_in_plan
                request_id: 1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f
    InvitationConflict:
      description: >
        The adjuster is already a member of your firm (`already_member`), or
        already has a

        pending invitation (`invitation_pending`).
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            already_member:
              summary: already_member
              value:
                type: https://docs.osaconnection.com/errors#already_member
                title: Already a member
                status: 409
                detail: This adjuster is already a member of your firm.
                code: already_member
                request_id: 5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d
            invitation_pending:
              summary: invitation_pending
              value:
                type: https://docs.osaconnection.com/errors#invitation_pending
                title: Invitation pending
                status: 409
                detail: This adjuster already has a pending invitation from your firm.
                code: invitation_pending
                request_id: 6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e
    PayloadTooLarge:
      description: The body is larger than 1 MB (`payload_too_large`).
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            payload_too_large:
              summary: payload_too_large
              value:
                type: https://docs.osaconnection.com/errors#payload_too_large
                title: Payload too large
                status: 413
                detail: Request bodies are limited to 1 MB.
                code: payload_too_large
                request_id: 1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e
    InvitationUnprocessable:
      description: >
        The invitation can't be sent. `validation_failed`: the request is
        invalid.

        `unknown_osaid`: OSA doesn't know this OSAID; check it with the
        adjuster, who can find

        it in the OSA app once signed in. With a test key, every OSAID that
        isn't one of your

        firm's test accounts gets `unknown_osaid`. `environment_mismatch`: a
        live key named a

        test account; invite test accounts with a test key.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            validation_failed:
              summary: validation_failed
              value:
                type: https://docs.osaconnection.com/errors#validation_failed
                title: Validation failed
                status: 422
                detail: The request has 1 invalid field.
                code: validation_failed
                request_id: 7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d
                errors:
                  - pointer: ''
                    detail: must have required property 'osa_id'
            unknown_osaid:
              summary: unknown_osaid
              value:
                type: https://docs.osaconnection.com/errors#unknown_osaid
                title: Unknown OSAID
                status: 422
                detail: No OSA user has this OSAID.
                code: unknown_osaid
                request_id: 8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e
            environment_mismatch:
              summary: environment_mismatch
              value:
                type: https://docs.osaconnection.com/errors#environment_mismatch
                title: Environment mismatch
                status: 422
                detail: Live keys can't invite test accounts. Use a test key.
                code: environment_mismatch
                request_id: c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9
    RateLimited:
      description: >
        Too many requests for this key (`rate_limited`). Wait for the number of
        seconds in

        `Retry-After`, then retry.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            rate_limited:
              summary: rate_limited
              value:
                type: https://docs.osaconnection.com/errors#rate_limited
                title: Rate limited
                status: 429
                detail: This key has exceeded 300 requests per minute.
                code: rate_limited
                request_id: 9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f
    InternalError:
      description: >
        Something failed on OSA's side (`internal_error`). Retry with
        exponential backoff; for

        `POST` requests, reuse the same `Idempotency-Key`. If it persists,
        contact OSA with the

        `request_id`.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            internal_error:
              summary: internal_error
              value:
                type: https://docs.osaconnection.com/errors#internal_error
                title: Internal error
                status: 500
                detail: Something went wrong on our side. Retry later.
                code: internal_error
                request_id: 2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: osa_live_… or osa_test_…
      description: >
        Send your API key as a bearer token: `Authorization: Bearer osa_live_…`.

        Keys are opaque; each belongs to exactly one firm and one purpose. Keys
        starting with

        `osa_test_` reach only test claims and test accounts. The scopes listed
        on each

        operation are the permissions the key needs; `GET /v1/me` shows the ones
        your key has.

````

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