openapi: 3.1.0
info:
  title: item API
  description: |
    The item API provides full CRUD access to your organization's data — People, Companies, and Custom Objects.

    Use it to:
    - Push structured data into item on a schedule
    - Sync custom field values across your stack
    - Pull filtered records and pre-configured views
    - Use item as your source of truth for customer data

    ## Authentication

    All requests require an API key passed in the `x-api-key` header.

    You can generate an API key from **Settings > System > API Key** in your item workspace.

    ```bash
    curl -H "x-api-key: sk_live_..." https://app.useitem.io/api/objects/contacts
    ```
  version: 1.0.0
  contact:
    name: item Support

servers:
  - url: https://app.useitem.io
    description: Production
  - url: http://localhost:3000
    description: Local development

security:
  - apiKey: []

components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: Your organization's API key (starts with `sk_live_`)

  schemas:
    Pagination:
      type: object
      properties:
        total:
          type: integer
          description: Total number of records matching the query
          example: 150
        limit:
          type: integer
          description: Maximum records returned per page
          example: 50
        offset:
          type: integer
          description: Number of records skipped
          example: 0
        has_more:
          type: boolean
          description: Whether more records exist beyond this page
          example: true

    ObjectRecord:
      type: object
      description: |
        A flat object containing all fields. System fields (name, email, phone, etc.) and
        custom fields are merged into a single level — the `custom_fields` JSON column is
        flattened so every field is a top-level key.
      properties:
        id:
          type: integer
          description: Unique record ID
          example: 123
        name:
          type: string
          example: "Alex Johnson"
        email:
          type: string
          example: "alex@acme.com"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      additionalProperties: true

    BatchObjectInput:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          description: Name of the object to create or update
          example: "Alex Johnson"
        match_by:
          type: string
          enum: [id, email, name]
          description: |
            How to find an existing record to update.
            - `id` — match by record ID (all object types)
            - `email` — match by email (contacts only)
            - `name` — match by exact name (all object types)
        match_value:
          oneOf:
            - type: string
            - type: integer
          description: The value to match against (required when `match_by` is set)
          example: "alex@acme.com"
        fields:
          type: object
          additionalProperties: true
          description: Key-value pairs of fields to set
          example:
            email: "alex@acme.com"
            role: "Engineer"
            custom_score: 95
        profile_image_url:
          type: string
          format: uri
          description: URL for avatar (contacts) or logo (companies)

    BatchResultitem:
      type: object
      properties:
        id:
          type: integer
          nullable: true
          description: Record ID (null if failed)
          example: 123
        status:
          type: string
          enum: [created, updated, failed]
          example: "updated"
        error:
          type: string
          description: Error message (only present when status is `failed`)

    BatchSummary:
      type: object
      properties:
        total:
          type: integer
          example: 3
        created:
          type: integer
          example: 1
        updated:
          type: integer
          example: 1
        failed:
          type: integer
          example: 1

    ViewSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        name:
          type: string
          example: "Active Customers"
        view_type:
          type: string
          enum: [table, kanban]
          example: "table"
        columns:
          type: array
          items:
            type: string
          example: ["name", "email", "industry", "custom_score"]

    Error:
      type: object
      properties:
        error:
          type: string
          example: "Object not found"

    ObjectTypeSchema:
      type: object
      properties:
        id:
          type: integer
          example: 1
        slug:
          type: string
          example: "contacts"
        display_name:
          type: string
          example: "People"
        plural_display_name:
          type: string
          nullable: true
          example: "People"
        description:
          type: string
          nullable: true
        icon:
          type: string
          nullable: true
        fields:
          type: array
          items:
            $ref: "#/components/schemas/FieldDefinition"

    FieldDefinition:
      type: object
      properties:
        field_name:
          type: string
          example: "industry"
        display_name:
          type: string
          example: "Industry"
        field_type:
          type: string
          example: "select"
        field_order:
          type: integer
          example: 5
        is_required:
          type: boolean
          example: false
        description:
          type: string
          nullable: true
        select_options:
          description: Available options for select/multi-select fields
          nullable: true
          type: array
          items:
            type: object
            properties:
              label:
                type: string
                example: "Technology"
              value:
                type: string
                example: "technology"
              color:
                type: string
                nullable: true
                example: "#3b82f6"
        allow_multiple:
          type: boolean
          nullable: true
        related_object_type_id:
          type: integer
          nullable: true
          description: For relationship fields, the ID of the related object type
        relationship_type:
          type: string
          nullable: true
          enum: [one_to_one, one_to_many, many_to_one, many_to_many, null]
        number_min:
          type: number
          nullable: true
          description: Optional minimum for Number fields, or minimum in percentage units for Percentage fields
        number_max:
          type: number
          nullable: true
          description: Optional maximum for Number fields, or maximum in percentage units for Percentage fields
        number_decimal_places:
          type: integer
          nullable: true
        currency_decimal_places:
          type: integer
          nullable: true
        percentage_decimal_places:
          type: integer
          minimum: 0
          maximum: 6
          nullable: true
          description: Editable and display precision for percentage fields
        value_scale:
          type: string
          nullable: true
          enum: [percentage_units, null]
          description: Percentage values use percentage units; 43.5 means 43.5%
        default_value:
          type: string
          nullable: true
        visibility_type:
          type: string
          example: "visible"

    OrganizationUser:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: User ID (auth UUID)
        full_name:
          type: string
          nullable: true
          example: "Alex Johnson"
        access_level:
          type: string
          enum: [admin, member]
          description: "'member' for standard users, 'admin' for organization admins and platform admins"
          example: "member"

  parameters:
    objectType:
      name: objectType
      in: path
      required: true
      description: |
        The object type slug. Use `contacts` for People, `companies` for Companies,
        or the slug of a Custom Object (e.g., `deals`, `tickets`).

        Singular forms (`contact`, `company`) are also accepted.
      schema:
        type: string
      examples:
        contacts:
          value: contacts
          summary: People
        companies:
          value: companies
          summary: Companies
        custom:
          value: deals
          summary: Custom object

paths:
  /api/objects/{objectType}:
    parameters:
      - $ref: "#/components/parameters/objectType"

    get:
      operationId: listOrGetObjects
      summary: List objects or fetch by ID
      description: |
        **List mode** (no `id` or `email` param): Returns a paginated list of objects with optional search, filtering, and sorting.

        **Single-object mode** (with `id` or `email`): Returns a single object by ID or email lookup.
      tags: [Objects]
      parameters:
        - name: id
          in: query
          description: Fetch a single object by ID. When provided, returns one object instead of a list.
          schema:
            type: integer
          example: 123
        - name: email
          in: query
          description: Fetch a contact by email (contacts only). When provided, returns one object.
          schema:
            type: string
          example: "alex@acme.com"
        - name: limit
          in: query
          description: "Maximum records to return (list mode only). Default: 50, max: 200."
          schema:
            type: integer
            default: 50
            maximum: 200
        - name: offset
          in: query
          description: Number of records to skip for pagination (list mode only).
          schema:
            type: integer
            default: 0
        - name: search
          in: query
          description: Text search on `name` (and `email` for contacts).
          schema:
            type: string
          example: "acme"
        - name: sort_by
          in: query
          description: "Field to sort by. Default: `created_at`."
          schema:
            type: string
            default: created_at
          example: "name"
        - name: sort_order
          in: query
          description: "Sort direction. Default: `desc`."
          schema:
            type: string
            enum: [asc, desc]
            default: desc
        - name: "filter[{field_name}]"
          in: query
          description: |
            Filter by field value (case-insensitive partial match). Works with both
            system fields and custom fields.

            Example: `filter[industry]=Technology`
          schema:
            type: string
        - name: include_all_fields
          in: query
          description: Include all system fields in the response (single-object mode only).
          schema:
            type: boolean
            default: false
        - name: include_summary
          in: query
          description: Include the AI-generated summary (contacts and companies only, single-object mode).
          schema:
            type: boolean
            default: false
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                oneOf:
                  - title: List response
                    type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/ObjectRecord"
                      pagination:
                        $ref: "#/components/schemas/Pagination"
                  - title: Single object response
                    type: object
                    properties:
                      data:
                        $ref: "#/components/schemas/ObjectRecord"
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Object type or record not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

    post:
      operationId: updateSingleField
      summary: Update a single custom field
      description: |
        Updates one custom field on an existing object. This is the legacy endpoint —
        for updating multiple fields at once, use `PATCH` instead.
      tags: [Objects]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - fieldName
                - fieldValue
              properties:
                id:
                  type: integer
                  description: Record ID (required for companies and custom objects)
                  example: 123
                email:
                  type: string
                  description: Contact email (alternative to `id` for contacts)
                  example: "alex@acme.com"
                fieldName:
                  type: string
                  description: Name of the custom field to update
                  example: "custom_score"
                fieldValue:
                  description: New value for the field
                  example: 95
      responses:
        "200":
          description: Field updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ObjectRecord"
                  message:
                    type: string
                    example: "Updated custom field 'custom_score' for contact"
        "400":
          description: Bad request or field not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Object not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

    put:
      operationId: createObject
      summary: Create an object
      description: |
        Creates a new object of the specified type.

        For contacts and companies, the system automatically deduplicates:
        - **Contacts** are matched by email — if a contact with the same email exists, it will be updated instead.
        - **Companies** are matched by domain, LinkedIn URL, or name similarity.

        Custom objects are always created as new records.
      tags: [Objects]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                  description: Display name for the object
                  example: "Alex Johnson"
                fields:
                  type: object
                  additionalProperties: true
                  description: Key-value pairs of fields to set on the object
                  example:
                    email: "alex@acme.com"
                    role: "Engineer"
                    industry: "Technology"
                    custom_score: 95
                profile_image_url:
                  type: string
                  format: uri
                  description: Avatar URL (contacts) or logo URL (companies)
      responses:
        "201":
          description: Object created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ObjectRecord"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "Validation failed: email: Invalid email format"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

    patch:
      operationId: updateObject
      summary: Update multiple fields
      description: |
        Updates one or more fields on an existing object in a single request.

        Supports both system fields (name, email, phone, etc.) and custom fields.
        Fields not included in the request are left unchanged.
      tags: [Objects]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: integer
                  description: Record ID (required unless using `email` for contacts)
                  example: 123
                email:
                  type: string
                  description: Contact email (alternative to `id`, contacts only)
                  example: "alex@acme.com"
                name:
                  type: string
                  description: Update the object name
                  example: "Alex M. Johnson"
                fields:
                  type: object
                  additionalProperties: true
                  description: Key-value pairs of fields to update
                  example:
                    role: "Senior Engineer"
                    custom_score: 100
                    industry: "Technology"
                profile_image_url:
                  type: string
                  format: uri
      responses:
        "200":
          description: Object updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ObjectRecord"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Object not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

    delete:
      operationId: deleteObject
      summary: Delete an object
      description: |
        Soft-deletes an object by ID. The record is marked as deleted but not
        permanently removed. All relationships involving this object are also soft-deleted.
      tags: [Objects]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: integer
                  description: Record ID to delete
                  example: 123
      responses:
        "200":
          description: Object deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
        "400":
          description: Bad request or deletion failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/objects/{objectType}/views:
    parameters:
      - $ref: "#/components/parameters/objectType"

    get:
      operationId: listViews
      summary: List available views
      description: |
        Returns all shared views configured for the given object type. Views are
        pre-configured filters, sorting, and column layouts created in the item UI.

        Use a view's `id` with the [Execute View](#tag/Views/operation/executeView)
        endpoint to pull filtered data.
      tags: [Views]
      responses:
        "200":
          description: List of shared views
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/ViewSummary"
              example:
                data:
                  - id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    name: "Active Customers"
                    view_type: "table"
                    columns: ["name", "email", "industry", "custom_score"]
                  - id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                    name: "By Stage"
                    view_type: "kanban"
                    columns: ["name", "stage", "value"]
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Object type not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/objects/{objectType}/views/{viewId}:
    parameters:
      - $ref: "#/components/parameters/objectType"
      - name: viewId
        in: path
        required: true
        description: The view ID (UUID) from the [List Views](#tag/Views/operation/listViews) endpoint.
        schema:
          type: string
          format: uuid

    get:
      operationId: executeView
      summary: Execute a view
      description: |
        Executes a shared view and returns paginated results. The view's filters,
        sorting, and column configuration are applied automatically.

        This is the recommended way to pull structured data from item — configure
        the view in the UI with the exact filters you need, then call this endpoint
        on a schedule.
      tags: [Views]
      parameters:
        - name: limit
          in: query
          description: "Maximum records to return. Default: 50, max: 200."
          schema:
            type: integer
            default: 50
            maximum: 200
        - name: offset
          in: query
          description: Number of records to skip for pagination.
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: View results
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/ObjectRecord"
                  view:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      name:
                        type: string
                  pagination:
                    $ref: "#/components/schemas/Pagination"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: View not found or not shared
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/objects/{objectType}/batch:
    parameters:
      - $ref: "#/components/parameters/objectType"

    post:
      operationId: batchUpsert
      summary: Batch create or update objects
      description: |
        Creates or updates up to 100 objects in a single request. Ideal for
        scheduled syncs (e.g., pushing data every 5 minutes).

        **Matching behavior:**
        - If `match_by` and `match_value` are provided and a record is found, it is **updated**.
        - If no match is found (or `match_by` is omitted), a new record is **created**.
        - For contacts and companies, creation also deduplicates automatically (by email/domain).

        **Error handling:**
        - Each object is processed independently — a failure on one object does not abort the batch.
        - Check the `results` array for per-object status.

        Objects are processed sequentially to ensure correct deduplication.
      tags: [Batch]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - objects
              properties:
                objects:
                  type: array
                  maxitems: 100
                  items:
                    $ref: "#/components/schemas/BatchObjectInput"
            example:
              objects:
                - name: "Alex Johnson"
                  match_by: "email"
                  match_value: "alex@acme.com"
                  fields:
                    email: "alex@acme.com"
                    role: "Senior Engineer"
                    custom_score: 95
                - name: "Acme Corp"
                  match_by: "id"
                  match_value: 456
                  fields:
                    website_url: "https://acme.com"
                    industry: "Technology"
                - name: "New Contact"
                  fields:
                    email: "new@example.com"
                    role: "Designer"
      responses:
        "200":
          description: Batch processed
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/BatchResultitem"
                  summary:
                    $ref: "#/components/schemas/BatchSummary"
              example:
                results:
                  - id: 123
                    status: "updated"
                  - id: 456
                    status: "updated"
                  - id: 789
                    status: "created"
                summary:
                  total: 3
                  created: 1
                  updated: 2
                  failed: 0
        "400":
          description: Invalid request (missing objects array, batch too large, etc.)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                too_large:
                  value:
                    error: "Batch size exceeds maximum of 100. Received 150 objects."
                missing_array:
                  value:
                    error: 'Request body must contain an "objects" array'
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Object type not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/meta/schema:
    get:
      operationId: getSchema
      summary: Get object schema
      description: |
        Returns all object types available to the organization along with their
        field definitions. Use this to discover what objects exist, what fields
        they have, and what select options are available.

        Includes both system object types (People, Companies) and any custom
        object types created by the organization.
      tags: [Schema]
      responses:
        "200":
          description: Schema returned
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/ObjectTypeSchema"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/meta/users:
    get:
      operationId: listOrganizationUsers
      summary: List organization users
      description: |
        Returns all users in the organization with their IDs and access levels.
        Useful for mapping `owned_by_user_id` values or assigning records to users.
      tags: [Users]
      responses:
        "200":
          description: Users returned
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/OrganizationUser"
              example:
                data:
                  - id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"

                    full_name: "Alex Johnson"
                    access_level: "admin"
                  - id: "b2c3d4e5-f6a7-8901-bcde-f12345678901"

                    full_name: "Sarah Chen"
                    access_level: "member"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/webhooks/{skillId}:
    parameters:
      - name: skillId
        in: path
        required: true
        description: The ID of the skill to trigger. The skill must have a webhook trigger configured and be in "live" status.
        schema:
          type: string
          format: uuid

    post:
      operationId: triggerSkillWebhook
      summary: Trigger a skill via webhook
      description: |
        Sends a webhook payload to trigger a skill (AI agent) run. The skill must have a
        webhook trigger configured and be in "live" status.

        The entire JSON body is passed to the skill as input context, so you can send
        any structured data your skill needs.

        **Common use cases:**
        - React to events from external services (Stripe, TypeForm, etc.)
        - Chain automation workflows across systems
        - Trigger skills from your own application on custom events

        **Optional signature verification:**
        For enhanced security, include an HMAC-SHA256 signature in the `x-webhook-signature`
        header. The signature is computed over the raw JSON body using your API key as the secret.
      tags: [Webhooks]
      parameters:
        - name: x-webhook-signature
          in: header
          required: false
          description: |
            Optional HMAC-SHA256 signature for payload verification.
            Format: `sha256={hex_signature}`.
            Computed as `HMAC-SHA256(raw_json_body, api_key)`.
          schema:
            type: string
          example: "sha256=a1b2c3d4e5f6..."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
              description: Any JSON object. Passed to the skill as input context.
            example:
              event: "new_lead"
              user:
                email: "alex@acme.com"
                name: "Alex Johnson"
              source: "landing_page"
              timestamp: "2025-10-20T12:00:00Z"
      responses:
        "200":
          description: Skill triggered successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  skillRunId:
                    type: string
                    format: uuid
                    description: ID of the created skill run
                    example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  message:
                    type: string
                    example: "Webhook received and skill triggered successfully"
        "400":
          description: Skill not active or no webhook trigger configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                not_active:
                  value:
                    error: "Skill is not active (status: paused)"
                no_trigger:
                  value:
                    error: "No webhook trigger configured for this skill. Please create a webhook trigger first."
                invalid_payload:
                  value:
                    error: "Payload must be a JSON object"
        "401":
          description: Invalid or missing API key, or invalid webhook signature
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Skill does not belong to your organization
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Skill not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "413":
          description: Payload too large (max 1MB)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

tags:
  - name: Objects
    description: Create, read, update, and delete People, Companies, and Custom Objects.
  - name: Schema
    description: Discover available object types and their field definitions.
  - name: Users
    description: List organization members and their access levels.
  - name: Views
    description: List and execute pre-configured views to pull filtered data.
  - name: Batch
    description: Bulk create or update objects for high-frequency sync operations.
  - name: Webhooks
    description: Trigger skills (AI agents) via HTTP webhooks from external systems.
