---
title: "List firms (customer-scoped, keyset-paginated)"
url: "https://developer-staging.agentsync.io/apis/identity/versions/1ed33a2c-924a-434c-9643-fbc531dea1b8/operations/listFirmsV2"
---

> Full API specification: https://developer-staging.agentsync.io/apis/identity/versions/1ed33a2c-924a-434c-9643-fbc531dea1b8.md

# List firms (customer-scoped, keyset-paginated)

`GET` `/v2/firms`

Operation ID: `listFirmsV2`

Returns a keyset-paginated list of firms visible to the authenticated customer, sorted by (modifiedDate ASC, id ASC). Optionally filters to records modified on or after `updated_since` (RFC3339 instant, inclusive). The FEIN is always masked to ****<last4>. Empty results return `items: []`, `page.size: 0`, `nextToken: null`. Returns 403 when the caller's token lacks the required scope.

## Query parameters

- `page_token` (string, optional) - Opaque continuation token returned by the previous response's `page.nextToken`. Omit on the first request. Format is server-controlled and may change without an API version bump.
- `page_size` (integer, int32, optional) - Requested number of items in the response. Defaults to 25 when omitted; values outside `[1, 250]` are rejected with `400` (not clamped). The actual size returned is reflected in `page.size` and may be smaller (last page or empty result).
- `updated_since` (string, date-time, optional) - RFC3339 timestamp. When supplied, only firms modified at or after this instant are returned.

## Responses

- `200` - Firm list retrieved successfully.
- `400` - One or more request fields failed validation
- `401` - No valid bearer token was presented
- `403` - The token does not carry the required scope
- `429` - Rate limit exceeded. The caller has exceeded the maximum number of requests allowed per rate-limit window for the authenticated credential. NOTE: The response body for this status deviates from the standard V2 error shape ({message, details, code}). The body contains only a top-level "message" string. Clients should treat the Retry-After header as the authoritative retry signal and must not parse the message string.
- `500` - An unexpected server error occurred

## OpenAPI definition

```yaml
openapi: 3.0.1
info:
  title: Identity API
  version: v1.275.8
servers:
  - url: https://api.sandbox.agentsync.io/id
    description: Sandbox server
  - url: https://api.agentsync.io/id
    description: Production server
paths:
  /v2/firms:
    get:
      summary: List firms (customer-scoped, keyset-paginated)
      description: >
        Returns a keyset-paginated list of firms visible to the authenticated
        customer, sorted by (modifiedDate ASC, id ASC). Optionally filters to
        records modified on or after `updated_since` (RFC3339 instant,
        inclusive).


        The FEIN is always masked to ****<last4>. Empty results return `items:
        []`, `page.size: 0`, `nextToken: null`.


        Returns 403 when the caller's token lacks the required scope.
      operationId: listFirmsV2
      tags:
        - Firms
      security:
        - oauth2:
            - identity.profiles.read
      parameters:
        - $ref: "#/components/parameters/PageToken"
        - $ref: "#/components/parameters/PageSize"
        - name: updated_since
          in: query
          required: false
          description: RFC3339 timestamp. When supplied, only firms modified at or after
            this instant are returned.
          schema:
            type: string
            format: date-time
            example: 2024-01-15T10:30:00Z
      responses:
        "200":
          description: Firm list retrieved successfully.
          headers:
            ratelimit-limit:
              $ref: "#/components/headers/ratelimit-limit"
            ratelimit-remaining:
              $ref: "#/components/headers/ratelimit-remaining"
            ratelimit-reset:
              $ref: "#/components/headers/ratelimit-reset"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FirmV2List"
        "400":
          $ref: "#/components/responses/V2BadRequest"
        "401":
          $ref: "#/components/responses/V2Unauthorized"
        "403":
          $ref: "#/components/responses/V2Forbidden"
        "429":
          $ref: "#/components/responses/V2TooManyRequests"
        "500":
          $ref: "#/components/responses/V2InternalServerError"
security:
  - oauth2:
      - identity.profiles.read
components:
  parameters:
    PageToken:
      name: page_token
      in: query
      required: false
      description: >
        Opaque continuation token returned by the previous response's
        `page.nextToken`. Omit on the first request. Format is server-controlled
        and may change without an API version bump.
      schema:
        type: string
    PageSize:
      name: page_size
      in: query
      required: false
      description: >
        Requested number of items in the response. Defaults to 25 when omitted;
        values outside `[1, 250]` are rejected with `400` (not clamped). The
        actual size returned is reflected in `page.size` and may be smaller
        (last page or empty result).
      schema:
        type: integer
        format: int32
        minimum: 1
        maximum: 250
        default: 25
  headers:
    ratelimit-limit:
      description: >
        Maximum number of requests allowed per rate-limit window for the
        authenticated credential. The value returned in this header is
        authoritative; do not cache it.
      schema:
        type: integer
    ratelimit-remaining:
      description: Number of requests remaining in the current rate-limit window.
      schema:
        type: integer
    ratelimit-reset:
      description: >
        Unix timestamp (seconds) after which at least one additional request
        slot becomes available. In a sliding window this is a hint, not a hard
        reset point — retrying exactly at this instant may still return 429
        under sustained load.
      schema:
        type: integer
    Retry-After:
      description: >
        Minimum seconds to wait before retrying. Use this value directly as your
        retry delay; it already accounts for any server-side backoff.
      schema:
        type: integer
  schemas:
    FirmV2List:
      title: FirmV2List
      description: Keyset-paginated list of firms (F5 envelope).
      type: object
      required:
        - items
        - page
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/FirmV2"
        page:
          $ref: "#/components/schemas/Page"
    FirmV2:
      title: FirmV2
      description: >
        Customer-facing firm resource. The FEIN is always masked to ****<last4>;
        the full FEIN is available only via GET /v2/firms/{firmId}/fein
        (audited).
      type: object
      required:
        - id
        - npn
        - createdAt
      properties:
        id:
          type: string
          format: uuid
          description: The unique identifier of the firm.
          readOnly: true
          example: 550e8400-e29b-41d4-a716-446655440000
        npn:
          type: string
          description: National Producer Number.
          readOnly: true
          example: "1234567"
        name:
          type: string
          description: Legal name of the firm.
          example: Acme Insurance LLC
        email:
          type: string
          format: email
          description: Primary contact email address.
          example: contact@acme.com
        type:
          type: string
          description: Business entity type (SC, LLC, SP).
          example: LLC
        finraCrdNumber:
          type: string
          description: FINRA CRD number (digits only, max 11 characters).
          example: "12345"
        fein:
          type: string
          description: >
            Masked Federal Employer Identification Number. Always returned as
            ****<last4>. The full FEIN is never exposed by this endpoint.
          readOnly: true
          example: "****6789"
        testAccount:
          type: boolean
          description: Whether this is a test account.
          example: false
        createdAt:
          type: string
          format: date-time
          description: ISO-8601 UTC timestamp when the firm was created.
          readOnly: true
          example: 2024-01-15T10:30:00Z
        updatedAt:
          type: string
          format: date-time
          description: ISO-8601 UTC timestamp of the last update.
          readOnly: true
          example: 2024-06-01T14:22:00Z
    Page:
      type: object
      description: Page metadata for a token-based list response.
      required:
        - size
        - nextToken
      properties:
        size:
          type: integer
          format: int32
          minimum: 0
          description: >
            Number of items actually returned in this response — equal to
            `items.length`. On an empty result this is `0`. This is NOT a copy
            of the request's `page_size` hint; clients holding the hint already
            know what they asked for.
          example: 25
        nextToken:
          type: string
          nullable: true
          description: >
            Opaque token to request the next page. `null` when this is the last
            page. Pass back as the `page_token` query parameter to resume.
          example: eyJ2IjoxLCJrIjp7InVwZGF0ZWRBdCI6IjIwMjYtMDUtMTlUMTI6MDA6MDBaIiwiaWQiOiIwMUhYWVoifX0
    V2Error:
      type: object
      required:
        - message
        - details
        - code
      properties:
        message:
          type: string
          description: Human-readable error summary. Do not parse — use "code" for
            branching.
          example: Validation failed
        details:
          type: array
          description: Per-field validation errors. Empty for all non-400 responses.
          items:
            $ref: "#/components/schemas/V2FieldError"
        code:
          type: string
          description: Stable machine-readable error code.
          enum:
            - validation_failed
            - unauthorized
            - unauthorized_scope
            - resource_not_found
            - conflict
            - unprocessable
            - unsupported_media_type
            - rate_limited
            - internal
          example: validation_failed
    V2FieldError:
      type: object
      required:
        - param
        - message
      properties:
        param:
          type: string
          description: The request field that failed validation
          example: email
        message:
          type: string
          description: Human-readable description of the failure
          example: must not be blank
  responses:
    V2BadRequest:
      description: One or more request fields failed validation
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            message: Validation failed
            code: validation_failed
            details:
              - param: email
                message: must not be blank
    V2Unauthorized:
      description: No valid bearer token was presented
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            message: Authentication required
            code: unauthorized
            details: []
    V2Forbidden:
      description: The token does not carry the required scope
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            message: Insufficient scope
            code: unauthorized_scope
            details: []
    V2TooManyRequests:
      description: >
        Rate limit exceeded. The caller has exceeded the maximum number of
        requests allowed per rate-limit window for the authenticated credential.

        NOTE: The response body for this status deviates from the standard V2
        error shape ({message, details, code}). The body contains only a
        top-level "message" string. Clients should treat the Retry-After header
        as the authoritative retry signal and must not parse the message string.
      headers:
        ratelimit-limit:
          $ref: "#/components/headers/ratelimit-limit"
        ratelimit-remaining:
          $ref: "#/components/headers/ratelimit-remaining"
        ratelimit-reset:
          $ref: "#/components/headers/ratelimit-reset"
        Retry-After:
          $ref: "#/components/headers/Retry-After"
      content:
        application/json:
          schema:
            type: object
            required:
              - message
            properties:
              message:
                type: string
          example:
            message: API rate limit exceeded
    V2InternalServerError:
      description: An unexpected server error occurred
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/V2Error"
          example:
            message: An unexpected error occurred
            code: internal
            details: []
  securitySchemes:
    oauth2:
      type: oauth2
      description: >
        OAuth2 client credentials. The `tokenUrl` below is the SANDBOX token
        endpoint; for production use `https://auth.agentsync.io/oauth2/token`.
        OpenAPI 3.0 permits only one token URL per flow, so both cannot be
        expressed here — see the Authentication section for the full environment
        table.
      flows:
        clientCredentials:
          tokenUrl: https://auth.sandbox.agentsync.io/oauth2/token
          scopes:
            identity.profiles.read: Read access to producer profile resources (V2 customer-facing)
            identity.profiles.write: Write access to producer profile resources (V2 customer-facing)
```
