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

# List object records

> Lists records for one object. Pagination uses limit, next_cursor, and has_more. Request again with next_cursor until has_more is false. A custom object read without search, in created_at or updated_at order, follows a keyset cursor and returns every record exactly once. The default order is created_at, newest first. One page of that read reads at most 1,000 records, so a page with filters can hold fewer records than limit, or none, while has_more is true. Do not use the page size to decide that the list is finished. A read with search, with a sort on another field, or on a built-in object such as Contacts pages by number, and is_capped is true when the engine window stops at 1,000 rows. Contacts list only finalized leads. A cursor works only with the order that issued it. A malformed or mismatched cursor returns 400.

**Required scopes:** `data_objects.read`


## OpenAPI

````yaml /api-reference/openapi.yaml get /api/v1/objects/{object_id}/records
openapi: 3.1.0
info:
  title: RoofKit Partner API
  version: 1.0.0
  summary: REST JSON API for external RoofKit partner integrations.
  contact:
    name: RoofKit Engineering
  description: >
    RoofKit Partner API v1 authenticates requests with workspace API keys. API
    keys bind each request to exactly one workspace; Partner API paths do not
    accept roofer_id, workspace_id, or tenant identifiers. Authenticated
    endpoints are limited to 60 requests per minute per API key, method, and
    route template; invalid authentication attempts are limited separately at 20
    requests per minute per client and endpoint. Date-time parameters and fields
    accept ISO 8601 with Z or a ±HH:MM offset, for example 2026-09-07T04:00:00Z
    or 2026-09-07T00:00:00-04:00. Both name the same instant and return the same
    result. Encode + as %2B in a query string. Responses return UTC.


    A date-time without an offset, for example 2026-09-07T04:00:00, is
    deprecated. Until the sunset on 2027-01-10T00:00:00Z it is read as UTC, and
    the response carries a `Deprecation` header (RFC 9745) and a `Sunset` header
    (RFC 8594). From the sunset, Z or a ±HH:MM offset is required. A date-time
    without an offset then returns 400 with the `VALIDATION_ERROR` code, and the
    response carries no `Deprecation` or `Sunset` header.


    Each API key holds only the scopes that a workspace owner or admin chose
    when the key was created, and its scopes never change. Each operation lists
    the scopes it requires in `x-required-scopes`. A new key in Settings starts
    on the Read-only preset: `leads.read`, `analytics.read`, `reports.read`, and
    `data_objects.read`. A Read-only key can call every read operation except
    exports, Sales Visualizer reads, and trashed Data Objects, and it cannot
    call any write operation. A call that needs a scope the key does not hold
    returns 403 `FORBIDDEN`, and `error.details.required_scope` names a missing
    scope.
servers:
  - url: https://api.roofkit.ai
    description: Canonical RoofKit Partner API origin
security:
  - ApiKeyBearer: []
tags:
  - name: Foundation
    description: Authentication and contract foundation endpoints.
  - name: Leads
    description: Partner CRM lead sync endpoints.
  - name: Attribution
    description: >-
      Partner marketing attribution, campaign analytics, and event ingestion
      endpoints.
  - name: Pipeline
    description: Partner-readable lead pipeline metadata.
  - name: Reports
    description: Partner report import, listing, fetch, export, and download endpoints.
  - name: Visualizer
    description: >-
      Partner Sales Visualizer creation, generation, hosted photo upload links,
      image reads, session history, and share-link endpoints.
  - name: Estimates
    description: Partner Budget Estimate creation and reads for the key-bound workspace.
  - name: Objects
    description: >-
      Partner Data Objects discovery, schema, records, relations, views, and
      admin endpoints.
paths:
  /api/v1/objects/{object_id}/records:
    get:
      tags:
        - Objects
      summary: List object records
      description: >-
        Lists records for one object. Pagination uses limit, next_cursor, and
        has_more. Request again with next_cursor until has_more is false. A
        custom object read without search, in created_at or updated_at order,
        follows a keyset cursor and returns every record exactly once. The
        default order is created_at, newest first. One page of that read reads
        at most 1,000 records, so a page with filters can hold fewer records
        than limit, or none, while has_more is true. Do not use the page size to
        decide that the list is finished. A read with search, with a sort on
        another field, or on a built-in object such as Contacts pages by number,
        and is_capped is true when the engine window stops at 1,000 rows.
        Contacts list only finalized leads. A cursor works only with the order
        that issued it. A malformed or mismatched cursor returns 400.
      operationId: List object records
      parameters:
        - $ref: '#/components/parameters/ObjectId'
        - $ref: '#/components/parameters/Search'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/ObjectViewId'
        - $ref: '#/components/parameters/ObjectFiltersJson'
        - $ref: '#/components/parameters/ObjectSortsJson'
      responses:
        '200':
          description: Record page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectRecordListResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  parameters:
    ObjectId:
      name: object_id
      in: path
      required: true
      description: Object id, system key such as system:leads, or slug from discovery.
      x-docs-test-value: system:leads
      schema:
        type: string
        minLength: 1
        maxLength: 200
    Search:
      name: search
      in: query
      schema:
        type: string
        maxLength: 200
    Cursor:
      name: cursor
      in: query
      description: >-
        Opaque cursor returned by list/export responses. Send it back unchanged;
        malformed cursors return 400.
      schema:
        type: string
        maxLength: 500
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 50
    ObjectViewId:
      name: view_id
      in: query
      schema:
        type: string
        maxLength: 200
    ObjectFiltersJson:
      name: filters_json
      in: query
      description: >-
        JSON text of an ObjectFilterQuery. Send an array of filters that must
        all match, or an object with filters and an advanced filter whose rules
        join with and or or. Each filter names a field in fieldId by its id or
        its key, as GET /api/v1/objects/{object_id} lists them. Operators:
        is_empty and is_not_empty take no value. equals, not_equals, contains,
        and has_reached take value. any_of takes an array of strings.
        greater_than and less_than take a number, and between takes value and
        valueTo. before and after take a date (YYYY-MM-DD) or an ISO 8601
        datetime and exclude the day they name. within_range takes value and
        valueTo and includes both days. A bare date covers the whole UTC day. A
        datetime with an offset is converted to UTC. Without filters_json the
        read applies the filters of view_id, or none. Invalid JSON, an unknown
        operator, a missing value, an unknown field, or a field that cannot be
        filtered returns 400 with the path of each issue. Example, records
        created in October 2026:
        [{"fieldId":"created_at","operator":"within_range","value":"2026-10-01","valueTo":"2026-10-31"}]
      schema:
        type: string
        maxLength: 20000
        contentMediaType: application/json
        contentSchema:
          $ref: '#/components/schemas/ObjectFilterQuery'
    ObjectSortsJson:
      name: sorts_json
      in: query
      description: >-
        JSON text of an array of ObjectSort. Each sort names a field in fieldId
        by its id or its key. Without sorts_json the read uses the sort of
        view_id, or created_at, newest first. Only one sort on created_at or
        updated_at keeps the keyset cursor. Any other sort pages by number.
        Invalid JSON, an unknown field, or a formula field returns 400. Example,
        oldest records first: [{"fieldId":"created_at","direction":"asc"}]
      schema:
        type: string
        maxLength: 20000
        contentMediaType: application/json
        contentSchema:
          type: array
          maxItems: 5
          items:
            $ref: '#/components/schemas/ObjectSort'
  schemas:
    ObjectRecordListResponse:
      type: object
      additionalProperties: false
      required:
        - records
        - pagination
      properties:
        records:
          type: array
          items:
            $ref: '#/components/schemas/ObjectRecord'
        pagination:
          $ref: '#/components/schemas/ObjectCursorPagination'
    ObjectRecord:
      type: object
      additionalProperties: false
      required:
        - id
        - object_id
        - display_label
        - values
        - relationships
        - created_at
        - updated_at
      properties:
        id:
          type: string
        object_id:
          type: string
        record_key:
          type:
            - string
            - 'null'
        display_label:
          type: string
        values:
          $ref: '#/components/schemas/JsonRecord'
        relationships:
          type: array
          items:
            $ref: '#/components/schemas/ObjectRecordRelationship'
        stage_history:
          description: >-
            The first date the record entered each option of each status field,
            oldest first. Present on record reads. A Contacts or Reports record
            has none.
          type: array
          items:
            $ref: '#/components/schemas/ObjectRecordStageEntry'
        stage_dates:
          description: >-
            The appointment, won, and lost dates of the record. Present on
            record reads. Null when the object has no status field.
          oneOf:
            - $ref: '#/components/schemas/ObjectRecordStageDates'
            - type: 'null'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ObjectCursorPagination:
      type: object
      additionalProperties: false
      required:
        - limit
        - next_cursor
        - has_more
      properties:
        limit:
          type: integer
          minimum: 1
          maximum: 10000
        next_cursor:
          type:
            - string
            - 'null'
        has_more:
          type: boolean
        is_capped:
          type: boolean
        total:
          type: integer
          minimum: 0
          description: >-
            Exact number of rows that match the request. Some reads include it
            and others do not, so treat it as optional.
    ErrorResponse:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          type: object
          additionalProperties: false
          required:
            - code
            - message
            - request_id
          properties:
            code:
              $ref: '#/components/schemas/ErrorCode'
            message:
              type: string
            request_id:
              type: string
            details:
              type: object
              additionalProperties: true
    JsonRecord:
      type: object
      additionalProperties:
        oneOf:
          - type: string
          - type: number
          - type: boolean
          - type: 'null'
          - type: array
            items:
              oneOf:
                - type: string
                - type: number
                - type: boolean
                - type: 'null'
    ObjectRecordRelationship:
      description: A link from this record to a record of another object.
      type: object
      additionalProperties: true
      properties:
        id:
          type: string
        relationship_definition_id:
          type: string
        field_id:
          type: string
        source_object_id:
          type: string
        source_record_id:
          type: string
        target_object_id:
          type: string
        target_object_slug:
          type: string
        target_record_id:
          type: string
          description: For a Contact link, the id that GET /api/v1/leads/{lead_id} takes.
        target_label:
          type: string
        target_attribution:
          description: >-
            The marketing attribution of a linked Contact, the same object as
            Lead.attribution. Present on record reads for Contact links only.
            Null when the Contact was deleted or erased.
          oneOf:
            - $ref: '#/components/schemas/Attribution'
            - type: 'null'
    ObjectRecordStageEntry:
      description: The first entry of the record into one status option.
      type: object
      additionalProperties: false
      required:
        - field_id
        - field_key
        - option_id
        - option_label
        - entered_at
      properties:
        field_id:
          type: string
        field_key:
          type: string
        option_id:
          type: string
        option_label:
          type: string
        entered_at:
          type: string
          format: date-time
    ObjectRecordStageDates:
      description: >-
        Milestone dates of the Stage field. A date uses the Stage match of the
        dashboard Appointments and Won metrics, so a date range counts the same
        records.
      type: object
      additionalProperties: false
      required:
        - appointment_at
        - won_at
        - lost_at
      properties:
        appointment_at:
          description: >-
            The first entry into an appointment stage, such as Appointment or
            Qualified. A visit stage such as Appointment made does not count.
          type:
            - string
            - 'null'
          format: date-time
        won_at:
          description: The first entry into a won stage, such as Won or Sold.
          type:
            - string
            - 'null'
          format: date-time
        lost_at:
          description: The first entry into a lost stage, such as Lost or Dead.
          type:
            - string
            - 'null'
          format: date-time
    ErrorCode:
      type: string
      enum:
        - UNAUTHORIZED
        - FORBIDDEN
        - RATE_LIMITED
        - VALIDATION_ERROR
        - NOT_FOUND
        - CONFLICT
        - INSUFFICIENT_CREDITS
        - INTERNAL_ERROR
    Attribution:
      type: object
      additionalProperties: false
      properties:
        source:
          type:
            - string
            - 'null'
        medium:
          type:
            - string
            - 'null'
        campaign:
          type:
            - string
            - 'null'
        content:
          type:
            - string
            - 'null'
        conversion_url:
          type:
            - string
            - 'null'
          format: uri
        entry:
          description: >-
            Website entry for this touch. Omitted when unset. Does not replace
            source.
          type: string
          enum:
            - button
            - embed
            - direct
  responses:
    ValidationError:
      description: Request payload, query, or required header is invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: Missing, malformed, revoked, deleted, or unknown API key.
      headers:
        WWW-Authenticate:
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: >-
        The API key lacks a required scope, or the workspace is not active. A
        scope failure names a missing scope in `details.required_scope`. When
        the key lookup refuses the call, `details.missing_scopes` lists every
        required scope that the key does not hold.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            missing_scope:
              summary: A Read-only key calls a write operation
              value:
                error:
                  code: FORBIDDEN
                  message: 'API key does not include the required scope: leads.write.'
                  request_id: req_123
                  details:
                    required_scope: leads.write
                    missing_scopes:
                      - leads.write
    NotFound:
      description: Resource not found in the key-bound workspace.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimited:
      description: Rate limit exceeded for this key and endpoint or invalid-auth bucket.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalError:
      description: Internal authentication or server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  headers:
    RateLimitLimit:
      description: Maximum requests allowed in the current window.
      schema:
        type: string
    RateLimitRemaining:
      description: Requests remaining in the current window.
      schema:
        type: string
    RateLimitReset:
      description: ISO timestamp when the current rate-limit window resets.
      schema:
        type: string
        format: date-time
    RetryAfter:
      description: Seconds until the caller should retry.
      schema:
        type: string
  securitySchemes:
    ApiKeyBearer:
      type: http
      scheme: bearer
      bearerFormat: RoofKit API key
      description: 'Use `Authorization: Bearer rk_live_...`.'

````

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