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

# Generate or attach a live Google Sheet

> **API keys:** only `mode: "generate"` is allowed. Attaching an existing spreadsheet (`mode: "attach"`) returns `403 api_key_not_allowed`.

**Required scope:** `intel.view`, and API keys additionally need `campaigns.edit`.

<Note>Requires a plan that includes business intelligence (basic tier). Otherwise the request is refused; see [Errors](/concepts/errors).</Note>


## OpenAPI

````yaml /api-reference/openapi.json post /campaigns/{id}/sheet
openapi: 3.0.3
info:
  title: Pineyard API
  version: 1.0.0
  description: >-
    Programmatic access to agents, campaigns, calls, phone numbers, WhatsApp and
    results in your Pineyard workspace.
servers:
  - url: https://api.pineyard.ai
    description: Production
security:
  - bearerAuth: []
paths:
  /campaigns/{id}/sheet:
    post:
      summary: Generate or attach a live Google Sheet
      description: >-
        **API keys:** only `mode: "generate"` is allowed. Attaching an existing
        spreadsheet (`mode: "attach"`) returns `403 api_key_not_allowed`.
      parameters:
        - schema:
            type: string
          required: true
          name: id
          in: path
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                mode:
                  type: string
                  enum:
                    - generate
                    - attach
                spreadsheetUrl:
                  type: string
                sheetGid:
                  type: integer
                matchColumn:
                  type: string
                configuration:
                  type: object
                  properties:
                    version:
                      type: number
                      enum:
                        - 1
                    layoutLocked:
                      type: boolean
                      default: true
                    columns:
                      type: array
                      items:
                        type: object
                        properties:
                          key:
                            type: string
                            minLength: 1
                          label:
                            type: string
                            minLength: 1
                            maxLength: 100
                        required:
                          - key
                          - label
                      minItems: 1
                      maxItems: 100
                    filters:
                      type: object
                      properties:
                        interestLevel:
                          type: array
                          items:
                            type: string
                          maxItems: 50
                        qualification:
                          type: array
                          items:
                            type: string
                          maxItems: 50
                        callResult:
                          type: array
                          items:
                            type: string
                          maxItems: 50
                        callbackDue:
                          type: boolean
                        search:
                          type: string
                          maxLength: 200
                        metricFilters:
                          type: array
                          items:
                            type: object
                            properties:
                              key:
                                type: string
                              values:
                                type: array
                                items:
                                  type: string
                                maxItems: 20
                            required:
                              - key
                              - values
                          maxItems: 20
                        minDurationSec:
                          type: integer
                          minimum: 0
                          maximum: 86400
                        maxDurationSec:
                          type: integer
                          minimum: 0
                          maximum: 86400
                        minAttempts:
                          type: integer
                          minimum: 0
                          maximum: 100
                        calledFrom:
                          type: string
                        calledTo:
                          type: string
                        includeUnscored:
                          type: boolean
                        disposition:
                          type: array
                          items:
                            type: string
                          maxItems: 50
                        tags:
                          type: array
                          items:
                            type: string
                          maxItems: 50
                      default: {}
                    filterViewId:
                      type: integer
                    knownColumnKeys:
                      type: array
                      items:
                        type: string
                      maxItems: 200
                  required:
                    - version
                    - columns
              required:
                - mode
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  connected:
                    type: boolean
                  googleEmail:
                    type: string
                    nullable: true
                  linked:
                    type: boolean
                  spreadsheetUrl:
                    type: string
                    nullable: true
                  source:
                    type: string
                    nullable: true
                    enum:
                      - GENERATED
                      - ATTACHED
                  status:
                    type: string
                    nullable: true
                    enum:
                      - ACTIVE
                      - PAUSED
                      - ERROR
                  autoSync:
                    type: boolean
                  matchColumn:
                    type: string
                    nullable: true
                  lastSyncedAt:
                    type: string
                    nullable: true
                  lastError:
                    type: string
                    nullable: true
                  legacyLayout:
                    type: boolean
                  managedColumnCount:
                    type: integer
                  layoutLocked:
                    type: boolean
                  sheetTitle:
                    type: string
                    nullable: true
                  sheetGid:
                    type: integer
                    nullable: true
                  previousSpreadsheetUrl:
                    type: string
                    nullable: true
                  rotatedAt:
                    type: string
                    nullable: true
                required:
                  - connected
                  - googleEmail
                  - linked
                  - spreadsheetUrl
                  - source
                  - status
                  - autoSync
                  - matchColumn
                  - lastSyncedAt
                  - lastError
                  - legacyLayout
                  - managedColumnCount
                  - layoutLocked
                  - sheetTitle
                  - sheetGid
                  - previousSpreadsheetUrl
                  - rotatedAt
        '400':
          description: >-
            - `attached_identity_column_not_writable`: Identity column not
            writable on attached sheets.

            - `duplicate_sheet_column`: Duplicate column key.

            - `duplicate_sheet_header`: Duplicate header label.

            - `google_sheets_error`: Other Google Sheets write or read failure.

            - `google_token_unavailable`: Stored Google token has no refresh
            token.

            - `invalid_body`: The request body (or query) failed validation.
            Details are in `issues` (or `detail`). `422` on `POST
            /v1/campaigns/{campaignId}/leads`.

            - `invalid_sheet_url`: Spreadsheet URL not parseable.

            - `sheet_column_label_required`: A column has an empty label.

            - `sheet_columns_required`: Layout has no columns.

            - `sheet_forbidden`: Connected Google account cannot access the
            sheet.

            - `sheet_header_collision`: Managed labels collide with existing
            sheet headers.

            - `sheet_match_column_required`: Attached sheet needs a valid
            matchColumn.

            - `sheet_not_found`: Google says the spreadsheet does not exist.

            - `sheet_phone_column_required`: Layout lacks a phone column.

            - `sheets_not_connected`: No active Google Sheets connection.

            - `unknown_sheet_column`: Column key not in the field catalog.
        '401':
          description: >-
            - `invalid_api_key`: The API key is unknown, revoked or expired, or
            its workspace was deleted.

            - `unauthorized`: No `Authorization: Bearer` header, or an empty
            token.
        '403':
          description: >-
            - `api_key_not_allowed`: This action needs a person signed in to the
            dashboard (sign-ins, payments, email, and similar).

            - `api_key_not_scoped`: A key issued without scopes was used outside
            `/v1`.

            - `api_key_owner_inactive`: The member the key acts for was removed
            or suspended.

            - `feature_not_available`: This feature is not enabled for your
            workspace or plan.

            - `forbidden`: The key lacks the permission named in
            `requiredPermission`, or the resource belongs to another workspace.

            - `workspace_pending_approval`: The workspace has not been approved
            yet.

            - `workspace_suspended`: The workspace is suspended.
        '404':
          description: >-
            - `campaign_not_found`: No campaign with that id in this workspace.

            - `not_found`: The resource does not exist in your workspace, or the
            feature is not enabled for it.
        '409':
          description: '- `sheet_already_linked`: Campaign already has a sheet.'
        '429':
          description: '- `rate_limited`: Too many requests.'
        '500':
          description: >-
            - `internal_error`: An unexpected error. A `400` with this code
            usually means the body is not valid JSON; a `413` means the body is
            larger than 1 MB.
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Your API key, e.g. `pk_live_xxxxxxxxxxxxxxxxxxxxxxxx`.

````

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