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

# Integration patterns

> Production patterns for syncs, request approval, custom fields, uploads, and retries

# Integration patterns

Build each integration around stable resource IDs, explicit scopes, opaque cursors, and request-level observability.

## Read and reconcile

For a recurring synchronization:

1. Read the relevant list with the largest useful page size.
2. Keep filters and direction stable until the traversal completes.
3. Upsert downstream records by Canary resource ID.
4. Record `X-Request-ID` with each processed page.
5. Reconcile removed or archived records according to that resource's lifecycle.

Native ID cursors give stable forward traversal. Work-request and portal compatibility cursors encode offsets, so deduplication by ID protects against concurrent collection changes.

## Create a request and approve it

Create a work request with `POST /workrequests`, then review its compatibility status. When it is ready for work, create a native work order with `workRequestId`:

```bash theme={"theme":"github-light"}
curl --fail-with-body --request POST \
  "https://api.oncanary.com/v1/work-orders" \
  --header "Authorization: Bearer $CANARY_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "title": "Investigate reported vibration",
    "priority": "high",
    "workRequestId": "01JAY9XWBQ3R2H7V5F6N8K4MCP"
  }'
```

This operation approves the request and creates its linked work order atomically. Replaying approval for the same request returns the existing linked work order. Rejected requests return `409 invalid_work_request_state`.

## Write retries and idempotency

Public v1 has no general `Idempotency-Key` contract. A connection loss after a successful create can leave the client without the new ID.

Use these rules:

* Retry reads, validation-safe updates, and deletes after inspecting their documented lifecycle.
* Treat ordinary create requests as potentially duplicating records.
* Store the returned Canary ID immediately after every successful create.
* Reconcile uncertain creates with a read and a stable business identifier where the resource exposes one.
* Use `workRequestId` approval for its documented replay-safe behavior.
* Bound retries and log the request ID, HTTP status, and error code.

## Custom fields

Definitions are addressed by entity family and integer definition ID. Entity values are written in `extraFields` using the visible label as the key.

Fetch active definitions before validating integration-owned values. Definition labels must be unique within a resource family; collisions return `409 custom_field_label_conflict`.

Asset, part, and native work-order lists omit `extraFields`; fetch individual details for those values. Location lists accept `expand=extra_fields`.

## Work-request files

Attachment and thumbnail routes accept raw request bodies up to 1 MiB. Put the original filename in the URL path and set the media type in `Content-Type`.

The returned `publicUrl` is a signed private URL that expires after 60 minutes. Store `fileKey` and `filename` as durable metadata and request a fresh signed URL through a later work-request read.

Attachments and thumbnails can be changed only while a request is editable and pending. Lifecycle conflicts return `409 invalid_work_request_state`; oversized content returns `413`; unsupported picture data returns `415`.

## Archive and dependency behavior

Deletion semantics are resource-specific:

* Deleting a meter archives it and preserves its readings. Automation dependencies produce `409 resource_in_use`.
* Deleting a native work order can be blocked when historical records depend on it.
* Deleting a custom-field definition archives the definition.
* Retiring an identifier removes its active scan mappings.

Read current state before destructive operations and handle `409` as a reconciliation event.

## Operational checklist

* One API key per workload and deployment environment
* Minimum read/write scopes
* Dedicated organization or labeled fixtures for development calls
* Cursor traversal with stable filters
* Bounded concurrency within the API-key rate limit
* `Retry-After` handling for `429`
* Exponential backoff with jitter for transient `5xx`
* `X-Request-ID` in logs and support reports
* Resource-specific duplicate and archive handling
