Coperniq sites API

The sites API from Coperniq — 3 operation(s) for sites.

OpenAPI Specification

coperniq-sites-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: API Key accounts sites API
  version: 1.0.0
servers:
- url: https://api.coperniq.io/v1
  description: Production server
tags:
- name: sites
paths:
  /sites:
    get:
      operationId: list-sites
      summary: List Sites
      description: 'Retrieve a paginated list of sites. Results can be filtered by account, state, city, or zipcode.

        '
      tags:
      - sites
      parameters:
      - name: accountId
        in: query
        description: Filter by account ID.
        required: false
        schema:
          type: integer
      - name: state
        in: query
        description: Filter by state, case-insensitive (e.g. `ca` or `CA`).
        required: false
        schema:
          type: string
      - name: city
        in: query
        description: Filter by city, case-insensitive substring match (e.g. `francisco` matches `San Francisco`).
        required: false
        schema:
          type: string
      - name: zipcode
        in: query
        description: Filter by zip code, case-insensitive.
        required: false
        schema:
          type: string
      - name: page
        in: query
        description: Page number (1-based)
        required: false
        schema:
          type: integer
          default: 1
      - name: page_size
        in: query
        description: Number of items per page (max 100)
        required: false
        schema:
          type: integer
          default: 20
      - name: x-api-key
        in: header
        required: true
        schema:
          type: string
      responses:
        '200':
          description: List of sites
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Site'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListSitesRequestBadRequestError'
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListSitesRequestUnauthorizedError'
    post:
      operationId: create-site
      summary: Create Site
      description: 'Create a new site record belonging to an account.


        Required fields:

        - `accountId`: The account this site belongs to.

        - `fullAddress` or `geoLocation`: At least one must be provided. The backend geocodes `fullAddress` to derive coordinates.


        If a site with the same address already exists for the same account, the existing site is returned instead of creating a duplicate (`200` instead of `201`).

        '
      tags:
      - sites
      parameters:
      - name: x-api-key
        in: header
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Duplicate site found — existing site returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Site'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateSiteRequestBadRequestError'
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateSiteRequestUnauthorizedError'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SiteCreate'
  /sites/{siteId}:
    get:
      operationId: get-site
      summary: Get Site
      description: Retrieve a specific site by ID. Returns 404 if not found or if the site belongs to a different company.
      tags:
      - sites
      parameters:
      - name: siteId
        in: path
        required: true
        schema:
          type: string
      - name: x-api-key
        in: header
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Site details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Site'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetSiteRequestBadRequestError'
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetSiteRequestUnauthorizedError'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetSiteRequestNotFoundError'
    patch:
      operationId: update-site
      summary: Update Site
      description: 'Update an existing site. All fields are optional — only provided fields are changed.


        > **Moving a site to a different account:** Use `POST /sites/{siteId}/move` instead. `accountId` cannot be changed on a site update.


        > **Note on `geoLocation` and `fullAddress`:** Sending either field on update triggers geocoding — the backend may overwrite the stored coordinates with what Google Maps snaps to for the nearest address, so the values you send may not be stored as-is. There is currently no way to update coordinates without allowing the geocoder to potentially adjust them.

        '
      tags:
      - sites
      parameters:
      - name: siteId
        in: path
        required: true
        schema:
          type: string
      - name: x-api-key
        in: header
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Site updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Site'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateSiteRequestBadRequestError'
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateSiteRequestUnauthorizedError'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateSiteRequestNotFoundError'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SiteUpdate'
    delete:
      operationId: delete-site
      summary: Delete Site
      description: Delete a specific site by ID. Returns 404 if not found.
      tags:
      - sites
      parameters:
      - name: siteId
        in: path
        required: true
        schema:
          type: string
      - name: x-api-key
        in: header
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Successful response
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteSiteRequestUnauthorizedError'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteSiteRequestNotFoundError'
  /sites/{siteId}/move:
    post:
      operationId: move-site
      summary: Move Site to Another Account
      description: 'Move a site and all its associated records to a different account.


        **A new site is created with a new ID.** The original site (`siteId` in the path) is permanently

        deleted. The response contains the new site object — update any stored site IDs accordingly.


        The `fullAddress` and `title` are copied from the original site to the new one. The backend geocodes the address on the new site, so all address components (street, city, state, zipcode) are derived from geocoding.


        **Migrated records:** opportunities, projects, work orders, forms, services, systems, assets, and financial documents.


        > ⚠️ **This operation is irreversible.** The original site and its ID are permanently deleted.

        > Any references to the old `siteId` will no longer resolve.

        > Service plan instance links are removed — plans are account-scoped and cannot be transferred.

        '
      tags:
      - sites
      parameters:
      - name: siteId
        in: path
        description: Site identifier
        required: true
        schema:
          type: integer
      - name: x-api-key
        in: header
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The newly created site on the target account. This site has a new `id` — the original site has been deleted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Site'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MoveSiteRequestBadRequestError'
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MoveSiteRequestUnauthorizedError'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MoveSiteRequestNotFoundError'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                accountId:
                  type: integer
                  description: The target account ID to move the site to.
              required:
              - accountId
components:
  schemas:
    SitesSiteIdDeleteResponsesContentApplicationJsonSchemaCode:
      type: string
      enum:
      - NOT_FOUND
      title: SitesSiteIdDeleteResponsesContentApplicationJsonSchemaCode
    UpdateSiteRequestUnauthorizedError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesSiteIdPatchResponsesContentApplicationJsonSchemaCode'
      title: UpdateSiteRequestUnauthorizedError
    SitesGetResponsesContentApplicationJsonSchemaCode:
      type: string
      enum:
      - UNAUTHORIZED
      title: SitesGetResponsesContentApplicationJsonSchemaCode
    CreateSiteRequestUnauthorizedError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesPostResponsesContentApplicationJsonSchemaCode'
      title: CreateSiteRequestUnauthorizedError
    GetSiteRequestUnauthorizedError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesSiteIdGetResponsesContentApplicationJsonSchemaCode'
      title: GetSiteRequestUnauthorizedError
    GetSiteRequestBadRequestError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesSiteIdGetResponsesContentApplicationJsonSchemaCode'
        field:
          type: string
          description: Field that caused the validation error (if applicable)
      title: GetSiteRequestBadRequestError
    SitesSiteIdGetResponsesContentApplicationJsonSchemaCode:
      type: string
      enum:
      - NOT_FOUND
      title: SitesSiteIdGetResponsesContentApplicationJsonSchemaCode
    SitesSiteIdMovePostResponsesContentApplicationJsonSchemaCode:
      type: string
      enum:
      - NOT_FOUND
      title: SitesSiteIdMovePostResponsesContentApplicationJsonSchemaCode
    SitesPostResponsesContentApplicationJsonSchemaCode:
      type: string
      enum:
      - UNAUTHORIZED
      title: SitesPostResponsesContentApplicationJsonSchemaCode
    ListSitesRequestBadRequestError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesGetResponsesContentApplicationJsonSchemaCode'
        field:
          type: string
          description: Field that caused the validation error (if applicable)
      title: ListSitesRequestBadRequestError
    MoveSiteRequestBadRequestError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesSiteIdMovePostResponsesContentApplicationJsonSchemaCode'
        field:
          type: string
          description: Field that caused the validation error (if applicable)
      title: MoveSiteRequestBadRequestError
    DeleteSiteRequestNotFoundError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesSiteIdDeleteResponsesContentApplicationJsonSchemaCode'
      title: DeleteSiteRequestNotFoundError
    UpdateSiteRequestNotFoundError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesSiteIdPatchResponsesContentApplicationJsonSchemaCode'
      title: UpdateSiteRequestNotFoundError
    Site:
      type: object
      properties:
        id:
          type: integer
          description: Unique site ID.
        accountId:
          type: integer
          description: ID of the account this site belongs to.
        title:
          type:
          - string
          - 'null'
          description: Human-readable label for the site (e.g. "Main Office").
        fullAddress:
          type:
          - string
          - 'null'
          description: Full formatted address string.
        street:
          type:
          - string
          - 'null'
        city:
          type:
          - string
          - 'null'
        state:
          type:
          - string
          - 'null'
        zipcode:
          type:
          - string
          - 'null'
        geoLocation:
          type:
          - string
          - 'null'
          description: Latitude/longitude as "lat,lng" (e.g. "37.7749,-122.4194").
        timezone:
          type:
          - string
          - 'null'
          description: IANA timezone string (e.g. "America/Los_Angeles").
        createdAt:
          type:
          - string
          - 'null'
          format: date-time
        updatedAt:
          type:
          - string
          - 'null'
          format: date-time
      title: Site
    ListSitesRequestUnauthorizedError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesGetResponsesContentApplicationJsonSchemaCode'
      title: ListSitesRequestUnauthorizedError
    UpdateSiteRequestBadRequestError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesSiteIdPatchResponsesContentApplicationJsonSchemaCode'
        field:
          type: string
          description: Field that caused the validation error (if applicable)
      title: UpdateSiteRequestBadRequestError
    SiteUpdate:
      type: object
      properties:
        fullAddress:
          type: string
          description: Full address string. Backend will re-geocode when updated.
        geoLocation:
          type: string
          description: 'Latitude/longitude as "lat,lng". **On update**, sending this triggers a full re-geocode — the backend snaps to the nearest address via Google Maps, so the stored value may differ from what you sent.

            '
        title:
          type: string
        street:
          type: string
        city:
          type: string
        state:
          type: string
        zipcode:
          type: string
        timezone:
          type: string
          description: 'IANA timezone string (e.g. "America/Los_Angeles"). Use this to manually override the timezone if the geocoded value is incorrect or you are migrating from a system that has an explicit timezone already set.

            '
      description: 'All fields are optional. To move a site to a different account, use `POST /sites/{siteId}/move` instead of setting `accountId` here.

        '
      title: SiteUpdate
    MoveSiteRequestUnauthorizedError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesSiteIdMovePostResponsesContentApplicationJsonSchemaCode'
      title: MoveSiteRequestUnauthorizedError
    SiteCreate:
      type: object
      properties:
        accountId:
          type: integer
          description: ID of the account this site belongs to.
        fullAddress:
          type: string
          description: Full address string. Either `fullAddress` or `geoLocation` must be provided.
        geoLocation:
          type: string
          description: Latitude/longitude as "lat,lng". Either `fullAddress` or `geoLocation` must be provided.
        title:
          type: string
          description: Human-readable label for the site.
        street:
          type: string
        city:
          type: string
        state:
          type: string
        zipcode:
          type: string
      required:
      - accountId
      title: SiteCreate
    SitesSiteIdPatchResponsesContentApplicationJsonSchemaCode:
      type: string
      enum:
      - NOT_FOUND
      title: SitesSiteIdPatchResponsesContentApplicationJsonSchemaCode
    CreateSiteRequestBadRequestError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesPostResponsesContentApplicationJsonSchemaCode'
        field:
          type: string
          description: Field that caused the validation error (if applicable)
      title: CreateSiteRequestBadRequestError
    GetSiteRequestNotFoundError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesSiteIdGetResponsesContentApplicationJsonSchemaCode'
      title: GetSiteRequestNotFoundError
    DeleteSiteRequestUnauthorizedError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesSiteIdDeleteResponsesContentApplicationJsonSchemaCode'
      title: DeleteSiteRequestUnauthorizedError
    MoveSiteRequestNotFoundError:
      type: object
      properties:
        message:
          type: string
        code:
          $ref: '#/components/schemas/SitesSiteIdMovePostResponsesContentApplicationJsonSchemaCode'
      title: MoveSiteRequestNotFoundError
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic