{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Origo.ag API v2",
    "version": "0.3.1",
    "description": "## Authentication — start here\n\nThis is the **public, read-only reference** for Origo.ag API v2. API calls and\nmanagement require an approved **WireGuard** connection. The public website\ndoes not accept API requests or credentials. The current data integration is\na synthetic pilot; real partner activation and legacy customer GET migration\nremain separate work.\n\n### 1. Obtain access\n\nAsk Origo to commission your integration, issue a WireGuard profile and provide\nthe verified Origo TLS CA and a scoped API token. Tokens are tied to one\norganisation and site. **read** permits data GET. **push** permits ingestion\nand your own batch receipt GET. WireGuard keys, API tokens, management passwords\nand webhook signing keys serve different purposes.\n\n### 2. Connect and authenticate\n\nConnect your approved VPN. The private API origin is `https://10.79.0.1`.\nVerify TLS using the supplied Origo CA. Send\n`Authorization: Bearer YOUR_API_TOKEN` on every API call. Do not put tokens in\nURLs or source control. Your token is issued once, stored hashed by Origo,\nexpires after its configured lifetime, and can be revoked immediately.\n\n### 3. Check your token\n\nCall `GET https://10.79.0.1/api/v2/auth/me` over WireGuard. It returns only your\nclient scope, token permission/expiry and effective call capacity. It does\nnot query telemetry or grant access to uncommissioned integrations.\n\nFor interactive requests, open the\n[private Scalar reference](https://10.79.0.1/api/v2/docs/) after connecting to\nWireGuard and trusting the supplied TLS CA. Token entry and request testing\nare disabled on this public page. There is no hosted Scalar request proxy.\n\n### 4. Rotate, revoke and handle errors\n\nOrigo administrators/operators issue and revoke tokens in **Clients &\ncredentials**. For rotation without interruption, issue another token with\nthe same permission, deploy/test it, then revoke the old one. Immediate\nreplacement revokes the old token straight away. Revocation does not cancel\nalready accepted durable jobs. Management browser login is separate from\nAPI authentication. Legacy JWTs and password login are not accepted by v2.\n\n**401** indicates missing, invalid, expired or revoked credentials and carries\n`WWW-Authenticate: Bearer`. **403** indicates disabled access or insufficient\npermission/scope. **429** includes `Retry-After` seconds. API allowances are\nshared across a client's tokens; issuing extra tokens does not add capacity.\nAuthentication/receipt checks share a 30/minute control allowance and the\ncombined daily allowance. Global limits can reject before authentication.\n\n## Data and delivery\n\nThe eLynx examples are an Origo-designed synthetic contract, not a verified\nStockaID/Bunkmate export. Payload `schema_version: \"1.0\"` is independent of HTTP\nAPI version `v2`. Existing `/v1/` pilot aliases share tokens, receipts and\nquotas. GET serves verified published data. Webhook receivers verify the\ndocumented HMAC signature and TLS; only an approved synthetic receiver is\nenabled in this pilot.\n\nStandard begins at 10 PUSH/minute, 10 combined data GET/webhook attempts/minute,\n500 combined calls/UTC day, 2,000 new record versions/day, 64 KiB/request,\n100 records/batch, 100 rows/page and 10 MiB outgoing bodies/day. Paid capacity\nrequires configured pricing and manual commercial approval. Public\ndocumentation requests do not consume a customer's API allowance.\n"
  },
  "servers": [
    {
      "url": "https://10.79.0.1",
      "description": "Private API origin. WireGuard and the supplied Origo TLS CA are required."
    }
  ],
  "tags": [
    {
      "name": "Authentication",
      "description": "Start here: scoped partner bearer tokens, verification and lifecycle."
    },
    {
      "name": "eLynx ingestion",
      "description": "Synthetic partner PUSH; push permission required."
    },
    {
      "name": "eLynx receipts",
      "description": "The sender’s own durable batch status; push permission required."
    },
    {
      "name": "Data access",
      "description": "Verified published data; read permission required."
    },
    {
      "name": "Webhooks",
      "description": "Outbound signed deliveries; verify HMAC rather than sending an API token."
    }
  ],
  "paths": {
    "/api/v2/auth/me": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "operationId": "verifyPartnerAuthentication",
        "summary": "Verify your token and inspect its scope",
        "description": "Accepts either read or push permission. Requires an enabled client, but does not query telemetry or require a commissioned integration. No query parameters or request body. Consumes global and client control/daily call capacity. This endpoint verifies an already issued token; it does not create, exchange or refresh tokens.",
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authenticated principal and effective call allowance.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "no-store"
              },
              "X-Request-ID": {
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthenticationResult"
                },
                "example": {
                  "authenticated": true,
                  "client": {
                    "id": 1,
                    "name": "Example partner",
                    "organisation_id": "example-org",
                    "site_id": "example-site"
                  },
                  "credential": {
                    "prefix": "oa2_example",
                    "permission": "push",
                    "expires_at": "2027-01-01T00:00:00Z"
                  },
                  "integration_enabled": false,
                  "capacity": {
                    "plan": "Standard",
                    "push_calls_per_minute": 10,
                    "outgoing_calls_per_minute": 10,
                    "control_calls_per_minute": 30,
                    "calls_per_utc_day": 500
                  }
                }
              }
            }
          },
          "400": {
            "description": "Authentication or request rejected. Error body includes code, message and request_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, invalid, expired or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string",
                  "const": "Bearer"
                }
              }
            }
          },
          "403": {
            "description": "Valid credential but the client is disabled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Only GET is supported.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string",
                  "const": "GET"
                }
              }
            }
          },
          "429": {
            "description": "Global, client control or daily capacity exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Seconds to wait before retrying."
              }
            }
          },
          "503": {
            "description": "Durable control or metrics store temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "# Set ORIGO_BASE to the HTTPS origin shown in your browser.\n# Supply ORIGO_API_TOKEN securely in your environment.\ncurl --fail-with-body --cacert /path/to/origo-ca.crt \\\n  --header \"Authorization: Bearer $ORIGO_API_TOKEN\" \\\n  \"$ORIGO_BASE/api/v2/auth/me\""
          }
        ]
      }
    },
    "/api/v2/integrations/elynx/head-counts": {
      "post": {
        "operationId": "submitHeadCountBatch",
        "summary": "Submit eLynx head-counts",
        "description": "Deployed for enabled synthetic integrations. Requires WireGuard and a push credential scoped to this organisation/site; allowed sources are fixed by stream. Idempotency-Key must equal batch_id; replay exact original bytes. Business validation supplements the JSON schema. Required token permission: push.",
        "tags": [
          "eLynx ingestion"
        ],
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Must equal batch_id; unique across all streams within this partner/organisation/site. Bound to path and exact body bytes.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HeadCountBatch"
              },
              "examples": {
                "head-counts-am": {
                  "summary": "head counts am",
                  "value": {
                    "schema_version": "1.0",
                    "provider": "elynx",
                    "source_system": "stockaid",
                    "organisation_id": "haafco-demo",
                    "site_id": "dimboola-demo",
                    "batch_id": "elynx-stockaid-20261029-am-heads-001",
                    "extracted_at": "2026-10-29T06:10:00+11:00",
                    "records": [
                      {
                        "record_id": "stockaid-pen-001-20261029-am",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T06:00:00+11:00",
                        "pen_id": "P001",
                        "observed_at": "2026-10-29T06:00:00+11:00",
                        "head_count": 180
                      },
                      {
                        "record_id": "stockaid-pen-002-20261029-am",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T06:00:00+11:00",
                        "pen_id": "P002",
                        "observed_at": "2026-10-29T06:00:00+11:00",
                        "head_count": 160
                      },
                      {
                        "record_id": "stockaid-pen-003-20261029-am",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T06:00:00+11:00",
                        "pen_id": "P003",
                        "observed_at": "2026-10-29T06:00:00+11:00",
                        "head_count": 0
                      },
                      {
                        "record_id": "stockaid-pen-004-20261029-am",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T06:00:00+11:00",
                        "pen_id": "P004",
                        "observed_at": "2026-10-29T06:00:00+11:00",
                        "head_count": null
                      }
                    ]
                  }
                },
                "head-counts-pm": {
                  "summary": "head counts pm",
                  "value": {
                    "schema_version": "1.0",
                    "provider": "elynx",
                    "source_system": "stockaid",
                    "organisation_id": "haafco-demo",
                    "site_id": "dimboola-demo",
                    "batch_id": "elynx-stockaid-20261029-pm-heads-001",
                    "extracted_at": "2026-10-29T16:10:00+11:00",
                    "records": [
                      {
                        "record_id": "stockaid-pen-001-20261029-pm",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T16:00:00+11:00",
                        "pen_id": "P001",
                        "observed_at": "2026-10-29T16:00:00+11:00",
                        "head_count": 178
                      },
                      {
                        "record_id": "stockaid-pen-002-20261029-pm",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T16:00:00+11:00",
                        "pen_id": "P002",
                        "observed_at": "2026-10-29T16:00:00+11:00",
                        "head_count": 160
                      },
                      {
                        "record_id": "stockaid-pen-003-20261029-pm",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T16:00:00+11:00",
                        "pen_id": "P003",
                        "observed_at": "2026-10-29T16:00:00+11:00",
                        "head_count": 0
                      },
                      {
                        "record_id": "stockaid-pen-004-20261029-pm",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T16:00:00+11:00",
                        "pen_id": "P004",
                        "observed_at": "2026-10-29T16:00:00+11:00",
                        "head_count": null
                      }
                    ]
                  }
                },
                "head-count-correction": {
                  "summary": "head count correction",
                  "value": {
                    "schema_version": "1.0",
                    "provider": "elynx",
                    "source_system": "stockaid",
                    "organisation_id": "haafco-demo",
                    "site_id": "dimboola-demo",
                    "batch_id": "elynx-stockaid-20261029-heads-correction-001",
                    "extracted_at": "2026-10-29T08:30:00+11:00",
                    "records": [
                      {
                        "record_id": "stockaid-pen-004-20261029-am",
                        "revision": 2,
                        "status": "active",
                        "source_updated_at": "2026-10-29T08:20:00+11:00",
                        "pen_id": "P004",
                        "observed_at": "2026-10-29T06:00:00+11:00",
                        "head_count": 145
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Committed durably to inbox; awaiting complete Influx storage and publication. Same-byte queued/processing replay returns existing receipt.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "Relative receipt-status URL."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchReceipt"
                }
              }
            }
          },
          "200": {
            "description": "Identical replay of an already stored or failed batch; existing receipt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing/invalid idempotency header or unsupported schema version.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner/site/source/stream is outside credential scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency reuse, record revision conflict or stale unknown revision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Decoded body exceeds 1 MiB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Only UTF-8 application/json and agreed content encoding are accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Whole batch rejected for schema, mapping, time or business-rule errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until retry; honour without changing the original request bytes/key.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Cannot durably accept; no success receipt issued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Delay in seconds before retry; use for throttling or temporary inability to accept.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/integrations/elynx/ration-assignments": {
      "post": {
        "operationId": "submitRationAssignmentBatch",
        "summary": "Submit eLynx ration-assignments",
        "description": "Deployed for enabled synthetic integrations. Requires WireGuard and a push credential scoped to this organisation/site; allowed sources are fixed by stream. Idempotency-Key must equal batch_id; replay exact original bytes. Business validation supplements the JSON schema. Required token permission: push.",
        "tags": [
          "eLynx ingestion"
        ],
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Must equal batch_id; unique across all streams within this partner/organisation/site. Bound to path and exact body bytes.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RationAssignmentBatch"
              },
              "examples": {
                "ration-assignments": {
                  "summary": "ration assignments",
                  "value": {
                    "schema_version": "1.0",
                    "provider": "elynx",
                    "source_system": "feedbunk3000",
                    "organisation_id": "haafco-demo",
                    "site_id": "dimboola-demo",
                    "batch_id": "elynx-feedbunk-20261029-am-rations-001",
                    "extracted_at": "2026-10-29T06:10:00+11:00",
                    "records": [
                      {
                        "record_id": "feedbunk-assignment-pen-001-20261028",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T06:01:00+11:00",
                        "pen_id": "P001",
                        "effective_from": "2026-10-28T06:00:00+11:00",
                        "effective_to": null,
                        "assignment_status": "assigned",
                        "ration_id": "FIN-01",
                        "ration_name": "Finisher 1"
                      },
                      {
                        "record_id": "feedbunk-assignment-pen-002-20261028",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T06:01:00+11:00",
                        "pen_id": "P002",
                        "effective_from": "2026-10-28T06:00:00+11:00",
                        "effective_to": null,
                        "assignment_status": "assigned",
                        "ration_id": "GRO-02",
                        "ration_name": "Grower 2"
                      },
                      {
                        "record_id": "feedbunk-assignment-pen-003-20261028",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T06:01:00+11:00",
                        "pen_id": "P003",
                        "effective_from": "2026-10-28T06:00:00+11:00",
                        "effective_to": null,
                        "assignment_status": "unassigned",
                        "ration_id": null,
                        "ration_name": null
                      },
                      {
                        "record_id": "feedbunk-assignment-pen-004-20261028",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T06:01:00+11:00",
                        "pen_id": "P004",
                        "effective_from": "2026-10-28T06:00:00+11:00",
                        "effective_to": null,
                        "assignment_status": "unknown",
                        "ration_id": null,
                        "ration_name": null
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Committed durably to inbox; awaiting complete Influx storage and publication. Same-byte queued/processing replay returns existing receipt.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "Relative receipt-status URL."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchReceipt"
                }
              }
            }
          },
          "200": {
            "description": "Identical replay of an already stored or failed batch; existing receipt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing/invalid idempotency header or unsupported schema version.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner/site/source/stream is outside credential scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency reuse, record revision conflict or stale unknown revision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Decoded body exceeds 1 MiB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Only UTF-8 application/json and agreed content encoding are accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Whole batch rejected for schema, mapping, time or business-rule errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until retry; honour without changing the original request bytes/key.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Cannot durably accept; no success receipt issued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Delay in seconds before retry; use for throttling or temporary inability to accept.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/integrations/elynx/feed-quantities": {
      "post": {
        "operationId": "submitFeedQuantityBatch",
        "summary": "Submit eLynx feed-quantities",
        "description": "Deployed for enabled synthetic integrations. Requires WireGuard and a push credential scoped to this organisation/site; allowed sources are fixed by stream. Idempotency-Key must equal batch_id; replay exact original bytes. Business validation supplements the JSON schema. Required token permission: push.",
        "tags": [
          "eLynx ingestion"
        ],
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Must equal batch_id; unique across all streams within this partner/organisation/site. Bound to path and exact body bytes.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedQuantityBatch"
              },
              "examples": {
                "feed-quantities": {
                  "summary": "feed quantities",
                  "value": {
                    "schema_version": "1.0",
                    "provider": "elynx",
                    "source_system": "feedbunk3000",
                    "organisation_id": "haafco-demo",
                    "site_id": "dimboola-demo",
                    "batch_id": "elynx-feedbunk-20261029-am-feed-001",
                    "extracted_at": "2026-10-29T06:10:00+11:00",
                    "records": [
                      {
                        "record_id": "feedbunk-pen-001-20261028-FIN-01-delivered-asfed",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T06:02:00+11:00",
                        "pen_id": "P001",
                        "ration_id": "FIN-01",
                        "ration_name": "Finisher 1",
                        "period_start": "2026-10-28T00:00:00+11:00",
                        "period_end": "2026-10-29T00:00:00+11:00",
                        "period_complete": true,
                        "quantity_kg": 2520.0,
                        "quantity_type": "delivered",
                        "quantity_basis": "as_fed",
                        "dry_matter_percent": null
                      },
                      {
                        "record_id": "feedbunk-pen-002-20261028-GRO-02-delivered-asfed",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T06:02:00+11:00",
                        "pen_id": "P002",
                        "ration_id": "GRO-02",
                        "ration_name": "Grower 2",
                        "period_start": "2026-10-28T00:00:00+11:00",
                        "period_end": "2026-10-29T00:00:00+11:00",
                        "period_complete": true,
                        "quantity_kg": 2160.0,
                        "quantity_type": "delivered",
                        "quantity_basis": "as_fed",
                        "dry_matter_percent": null
                      }
                    ]
                  }
                },
                "feed-quantity-void": {
                  "summary": "feed quantity void",
                  "value": {
                    "schema_version": "1.0",
                    "provider": "elynx",
                    "source_system": "feedbunk3000",
                    "organisation_id": "haafco-demo",
                    "site_id": "dimboola-demo",
                    "batch_id": "elynx-feedbunk-20261029-feed-void-001",
                    "extracted_at": "2026-10-29T09:10:00+11:00",
                    "records": [
                      {
                        "record_id": "feedbunk-pen-002-20261028-GRO-02-delivered-asfed",
                        "revision": 2,
                        "status": "void",
                        "source_updated_at": "2026-10-29T09:00:00+11:00",
                        "pen_id": "P002",
                        "ration_id": "GRO-02",
                        "ration_name": "Grower 2",
                        "period_start": "2026-10-28T00:00:00+11:00",
                        "period_end": "2026-10-29T00:00:00+11:00",
                        "period_complete": true,
                        "quantity_kg": 2160.0,
                        "quantity_type": "delivered",
                        "quantity_basis": "as_fed",
                        "dry_matter_percent": null
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Committed durably to inbox; awaiting complete Influx storage and publication. Same-byte queued/processing replay returns existing receipt.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "Relative receipt-status URL."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchReceipt"
                }
              }
            }
          },
          "200": {
            "description": "Identical replay of an already stored or failed batch; existing receipt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing/invalid idempotency header or unsupported schema version.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner/site/source/stream is outside credential scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency reuse, record revision conflict or stale unknown revision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Decoded body exceeds 1 MiB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Only UTF-8 application/json and agreed content encoding are accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Whole batch rejected for schema, mapping, time or business-rule errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until retry; honour without changing the original request bytes/key.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Cannot durably accept; no success receipt issued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Delay in seconds before retry; use for throttling or temporary inability to accept.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/integrations/elynx/movements": {
      "post": {
        "operationId": "submitMovementBatch",
        "summary": "Submit eLynx movements",
        "description": "Deployed for enabled synthetic integrations. Requires WireGuard and a push credential scoped to this organisation/site; allowed sources are fixed by stream. Idempotency-Key must equal batch_id; replay exact original bytes. Business validation supplements the JSON schema. Required token permission: push.",
        "tags": [
          "eLynx ingestion"
        ],
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Must equal batch_id; unique across all streams within this partner/organisation/site. Bound to path and exact body bytes.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MovementBatch"
              },
              "examples": {
                "movements": {
                  "summary": "movements",
                  "value": {
                    "schema_version": "1.0",
                    "provider": "elynx",
                    "source_system": "stockaid",
                    "organisation_id": "haafco-demo",
                    "site_id": "dimboola-demo",
                    "batch_id": "elynx-stockaid-20261029-pm-moves-001",
                    "extracted_at": "2026-10-29T16:10:00+11:00",
                    "extract_start": "2026-10-29T06:00:00+11:00",
                    "extract_end": "2026-10-29T16:10:00+11:00",
                    "records": [
                      {
                        "record_id": "stockaid-movement-demo-1001",
                        "revision": 1,
                        "status": "active",
                        "source_updated_at": "2026-10-29T12:05:00+11:00",
                        "occurred_at": "2026-10-29T12:00:00+11:00",
                        "head_count": 2,
                        "movement_type": "departure",
                        "from_pen_id": "P001",
                        "to_pen_id": null
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Committed durably to inbox; awaiting complete Influx storage and publication. Same-byte queued/processing replay returns existing receipt.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "Relative receipt-status URL."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchReceipt"
                }
              }
            }
          },
          "200": {
            "description": "Identical replay of an already stored or failed batch; existing receipt.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchReceipt"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing/invalid idempotency header or unsupported schema version.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Partner/site/source/stream is outside credential scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency reuse, record revision conflict or stale unknown revision.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Decoded body exceeds 1 MiB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Only UTF-8 application/json and agreed content encoding are accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Whole batch rejected for schema, mapping, time or business-rule errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until retry; honour without changing the original request bytes/key.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Cannot durably accept; no success receipt issued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Delay in seconds before retry; use for throttling or temporary inability to accept.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/integrations/elynx/batches/{batch_id}": {
      "get": {
        "operationId": "getOwnElynxBatchReceipt",
        "summary": "Read own accepted batch status",
        "description": "Partner can read only receipts in its authorised organisation/site. Return 404 for another partner's receipt. Does not implement the legacy customer GET API. Required token permission: push.",
        "tags": [
          "eLynx receipts"
        ],
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "name": "batch_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current receipt; inspect status, stored_at and errors.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchReceipt"
                },
                "example": {
                  "batch_id": "elynx-stockaid-20261029-am-heads-001",
                  "status": "queued",
                  "received_at": "2026-10-28T19:10:01Z",
                  "record_count": 4,
                  "status_url": "/v1/integrations/elynx/batches/elynx-stockaid-20261029-am-heads-001"
                }
              }
            }
          },
          "401": {
            "description": "Invalid token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Receipt lookup not permitted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Receipt absent or inaccessible.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Lookup rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until retry; honour without changing the original request bytes/key.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Receipt store unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Delay in seconds before retry; use for throttling or temporary inability to accept.",
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/data/elynx/head-counts": {
      "get": {
        "operationId": "readHeadCountRecords",
        "summary": "Read published active head-counts",
        "tags": [
          "Data access"
        ],
        "description": "Requires a separate read credential; push/session credentials do not grant data access. Reads actual Influx points and verifies accepted hashes/exact timestamps; returns 503 on missing/corrupt storage, never inbox fallback. Source-time filtering uses observation/effective-from/period-start/occurrence time. Void revisions are excluded. Current pilot Influx retention is 30 days. Required token permission: read.",
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Inclusive source effective time. Default: seven elapsed days before now."
          },
          {
            "in": "query",
            "name": "until",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Exclusive source effective time. Default now; positive range at most 30 days."
          },
          {
            "in": "query",
            "name": "pen",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Mapped external pen ID; movement filtering uses its destination or source pen index."
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            },
            "description": "Bounded by client page allowance; Standard maximum 100."
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Opaque signed continuation, expires after one hour. Preserve the same query filters; not a historical snapshot across concurrent corrections."
          }
        ],
        "responses": {
          "200": {
            "description": "Verified published active records.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "organisation_id",
                    "site_id",
                    "stream",
                    "synthetic",
                    "verified_source",
                    "records",
                    "next_cursor",
                    "count",
                    "since",
                    "until"
                  ],
                  "properties": {
                    "organisation_id": {
                      "type": "string"
                    },
                    "site_id": {
                      "type": "string"
                    },
                    "stream": {
                      "const": "head-counts"
                    },
                    "synthetic": {
                      "const": true
                    },
                    "verified_source": {
                      "const": "influx-2/origo_elynx_demo_v2"
                    },
                    "records": {
                      "type": "array",
                      "maxItems": 500,
                      "items": {
                        "$ref": "#/components/schemas/HeadCountRecord"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "count": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 500
                    },
                    "since": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "until": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid filter, cursor or range",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing/expired/revoked credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Wrong permission or disabled integration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Client/global capacity exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Storage verification unavailable or direction paused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/data/elynx/ration-assignments": {
      "get": {
        "operationId": "readRationAssignmentRecords",
        "summary": "Read published active ration-assignments",
        "tags": [
          "Data access"
        ],
        "description": "Requires a separate read credential; push/session credentials do not grant data access. Reads actual Influx points and verifies accepted hashes/exact timestamps; returns 503 on missing/corrupt storage, never inbox fallback. Source-time filtering uses observation/effective-from/period-start/occurrence time. Void revisions are excluded. Current pilot Influx retention is 30 days. Required token permission: read.",
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Inclusive source effective time. Default: seven elapsed days before now."
          },
          {
            "in": "query",
            "name": "until",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Exclusive source effective time. Default now; positive range at most 30 days."
          },
          {
            "in": "query",
            "name": "pen",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Mapped external pen ID; movement filtering uses its destination or source pen index."
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            },
            "description": "Bounded by client page allowance; Standard maximum 100."
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Opaque signed continuation, expires after one hour. Preserve the same query filters; not a historical snapshot across concurrent corrections."
          }
        ],
        "responses": {
          "200": {
            "description": "Verified published active records.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "organisation_id",
                    "site_id",
                    "stream",
                    "synthetic",
                    "verified_source",
                    "records",
                    "next_cursor",
                    "count",
                    "since",
                    "until"
                  ],
                  "properties": {
                    "organisation_id": {
                      "type": "string"
                    },
                    "site_id": {
                      "type": "string"
                    },
                    "stream": {
                      "const": "ration-assignments"
                    },
                    "synthetic": {
                      "const": true
                    },
                    "verified_source": {
                      "const": "influx-2/origo_elynx_demo_v2"
                    },
                    "records": {
                      "type": "array",
                      "maxItems": 500,
                      "items": {
                        "$ref": "#/components/schemas/RationAssignmentRecord"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "count": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 500
                    },
                    "since": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "until": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid filter, cursor or range",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing/expired/revoked credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Wrong permission or disabled integration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Client/global capacity exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Storage verification unavailable or direction paused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/data/elynx/feed-quantities": {
      "get": {
        "operationId": "readFeedQuantityRecords",
        "summary": "Read published active feed-quantities",
        "tags": [
          "Data access"
        ],
        "description": "Requires a separate read credential; push/session credentials do not grant data access. Reads actual Influx points and verifies accepted hashes/exact timestamps; returns 503 on missing/corrupt storage, never inbox fallback. Source-time filtering uses observation/effective-from/period-start/occurrence time. Void revisions are excluded. Current pilot Influx retention is 30 days. Required token permission: read.",
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Inclusive source effective time. Default: seven elapsed days before now."
          },
          {
            "in": "query",
            "name": "until",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Exclusive source effective time. Default now; positive range at most 30 days."
          },
          {
            "in": "query",
            "name": "pen",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Mapped external pen ID; movement filtering uses its destination or source pen index."
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            },
            "description": "Bounded by client page allowance; Standard maximum 100."
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Opaque signed continuation, expires after one hour. Preserve the same query filters; not a historical snapshot across concurrent corrections."
          }
        ],
        "responses": {
          "200": {
            "description": "Verified published active records.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "organisation_id",
                    "site_id",
                    "stream",
                    "synthetic",
                    "verified_source",
                    "records",
                    "next_cursor",
                    "count",
                    "since",
                    "until"
                  ],
                  "properties": {
                    "organisation_id": {
                      "type": "string"
                    },
                    "site_id": {
                      "type": "string"
                    },
                    "stream": {
                      "const": "feed-quantities"
                    },
                    "synthetic": {
                      "const": true
                    },
                    "verified_source": {
                      "const": "influx-2/origo_elynx_demo_v2"
                    },
                    "records": {
                      "type": "array",
                      "maxItems": 500,
                      "items": {
                        "$ref": "#/components/schemas/FeedQuantityRecord"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "count": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 500
                    },
                    "since": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "until": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid filter, cursor or range",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing/expired/revoked credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Wrong permission or disabled integration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Client/global capacity exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Storage verification unavailable or direction paused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/data/elynx/movements": {
      "get": {
        "operationId": "readMovementRecords",
        "summary": "Read published active movements",
        "tags": [
          "Data access"
        ],
        "description": "Requires a separate read credential; push/session credentials do not grant data access. Reads actual Influx points and verifies accepted hashes/exact timestamps; returns 503 on missing/corrupt storage, never inbox fallback. Source-time filtering uses observation/effective-from/period-start/occurrence time. Void revisions are excluded. Current pilot Influx retention is 30 days. Required token permission: read.",
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Inclusive source effective time. Default: seven elapsed days before now."
          },
          {
            "in": "query",
            "name": "until",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Exclusive source effective time. Default now; positive range at most 30 days."
          },
          {
            "in": "query",
            "name": "pen",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Mapped external pen ID; movement filtering uses its destination or source pen index."
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            },
            "description": "Bounded by client page allowance; Standard maximum 100."
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Opaque signed continuation, expires after one hour. Preserve the same query filters; not a historical snapshot across concurrent corrections."
          }
        ],
        "responses": {
          "200": {
            "description": "Verified published active records.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "organisation_id",
                    "site_id",
                    "stream",
                    "synthetic",
                    "verified_source",
                    "records",
                    "next_cursor",
                    "count",
                    "since",
                    "until"
                  ],
                  "properties": {
                    "organisation_id": {
                      "type": "string"
                    },
                    "site_id": {
                      "type": "string"
                    },
                    "stream": {
                      "const": "movements"
                    },
                    "synthetic": {
                      "const": true
                    },
                    "verified_source": {
                      "const": "influx-2/origo_elynx_demo_v2"
                    },
                    "records": {
                      "type": "array",
                      "maxItems": 500,
                      "items": {
                        "$ref": "#/components/schemas/MovementRecord"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "count": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 500
                    },
                    "since": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "until": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid filter, cursor or range",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing/expired/revoked credential",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Wrong permission or disabled integration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Client/global capacity exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "503": {
            "description": "Storage verification unavailable or direction paused",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "PartnerBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Enter your full opaque API token without the Bearer prefix. Issued once in Clients & credentials; hashed at rest; expires after 1–365 days; independently revocable. Permission is read or push and is bound to a client organisation/site. WireGuard and verified TLS are also required. This is not a JWT, management password, WireGuard key or webhook signing key."
      }
    },
    "schemas": {
      "HeadCountRecord": {
        "type": "object",
        "properties": {
          "record_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "revision": {
            "type": "integer",
            "minimum": 1
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "void"
            ]
          },
          "source_updated_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "pen_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "observed_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "head_count": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          }
        },
        "required": [
          "record_id",
          "revision",
          "status",
          "source_updated_at",
          "pen_id",
          "observed_at",
          "head_count"
        ],
        "additionalProperties": false,
        "description": "Absolute snapshot. Null is unknown, zero explicitly empty. Validate revision and time conflicts server-side."
      },
      "RationAssignmentRecord": {
        "type": "object",
        "properties": {
          "record_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "revision": {
            "type": "integer",
            "minimum": 1
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "void"
            ]
          },
          "source_updated_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "pen_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "effective_from": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "effective_to": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
                "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
              },
              {
                "type": "null"
              }
            ]
          },
          "assignment_status": {
            "type": "string",
            "enum": [
              "assigned",
              "unassigned",
              "unknown"
            ]
          },
          "ration_id": {
            "anyOf": [
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
              },
              {
                "type": "null"
              }
            ]
          },
          "ration_name": {
            "anyOf": [
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 200
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "record_id",
          "revision",
          "status",
          "source_updated_at",
          "pen_id",
          "effective_from",
          "effective_to",
          "assignment_status",
          "ration_id",
          "ration_name"
        ],
        "additionalProperties": false,
        "allOf": [
          {
            "if": {
              "properties": {
                "assignment_status": {
                  "const": "assigned"
                }
              }
            },
            "then": {
              "properties": {
                "ration_id": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 128,
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
                },
                "ration_name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200
                }
              }
            },
            "else": {
              "properties": {
                "ration_id": {
                  "type": "null"
                },
                "ration_name": {
                  "type": "null"
                }
              }
            }
          }
        ],
        "description": "Independent effective assignment; validate end after start and no conflicting active intervals."
      },
      "FeedQuantityRecord": {
        "type": "object",
        "properties": {
          "record_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "revision": {
            "type": "integer",
            "minimum": 1
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "void"
            ]
          },
          "source_updated_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "pen_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "ration_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "ration_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "period_start": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "period_end": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "period_complete": {
            "type": "boolean"
          },
          "quantity_kg": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "quantity_type": {
            "type": "string",
            "enum": [
              "planned",
              "delivered"
            ]
          },
          "quantity_basis": {
            "type": "string",
            "enum": [
              "as_fed",
              "dry_matter"
            ]
          },
          "dry_matter_percent": {
            "type": [
              "number",
              "null"
            ],
            "exclusiveMinimum": 0,
            "maximum": 100
          }
        },
        "required": [
          "record_id",
          "revision",
          "status",
          "source_updated_at",
          "pen_id",
          "ration_id",
          "ration_name",
          "period_start",
          "period_end",
          "period_complete",
          "quantity_kg",
          "quantity_type",
          "quantity_basis"
        ],
        "additionalProperties": false,
        "description": "Period total, not feed-drop event. Validate finite values, positive period, revisions and overlapping total identities server-side."
      },
      "MovementRecord": {
        "type": "object",
        "properties": {
          "record_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "revision": {
            "type": "integer",
            "minimum": 1
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "void"
            ]
          },
          "source_updated_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "head_count": {
            "type": "integer",
            "minimum": 1
          },
          "movement_type": {
            "type": "string",
            "enum": [
              "arrival",
              "departure",
              "death",
              "transfer"
            ]
          },
          "from_pen_id": {
            "anyOf": [
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
              },
              {
                "type": "null"
              }
            ]
          },
          "to_pen_id": {
            "anyOf": [
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "record_id",
          "revision",
          "status",
          "source_updated_at",
          "occurred_at",
          "head_count",
          "movement_type",
          "from_pen_id",
          "to_pen_id"
        ],
        "additionalProperties": false,
        "allOf": [
          {
            "if": {
              "properties": {
                "movement_type": {
                  "const": "arrival"
                }
              }
            },
            "then": {
              "properties": {
                "from_pen_id": {
                  "type": "null"
                },
                "to_pen_id": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 128,
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "movement_type": {
                  "enum": [
                    "departure",
                    "death"
                  ]
                }
              }
            },
            "then": {
              "properties": {
                "from_pen_id": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 128,
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
                },
                "to_pen_id": {
                  "type": "null"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "movement_type": {
                  "const": "transfer"
                }
              }
            },
            "then": {
              "properties": {
                "from_pen_id": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 128,
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
                },
                "to_pen_id": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 128,
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
                }
              }
            }
          }
        ],
        "description": "Validate transfer endpoints differ, mappings, source-update extraction window and event/revision semantics server-side."
      },
      "HeadCountBatch": {
        "type": "object",
        "properties": {
          "schema_version": {
            "type": "string",
            "const": "1.0"
          },
          "provider": {
            "type": "string",
            "const": "elynx"
          },
          "source_system": {
            "type": "string",
            "const": "stockaid"
          },
          "organisation_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "site_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "batch_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "extracted_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "records": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/HeadCountRecord"
            }
          }
        },
        "required": [
          "schema_version",
          "provider",
          "source_system",
          "organisation_id",
          "site_id",
          "batch_id",
          "extracted_at",
          "records"
        ],
        "additionalProperties": false,
        "description": "Whole-batch structural and business validation. The token determines allowed scope. See the written contract for cross-field rules."
      },
      "RationAssignmentBatch": {
        "type": "object",
        "properties": {
          "schema_version": {
            "type": "string",
            "const": "1.0"
          },
          "provider": {
            "type": "string",
            "const": "elynx"
          },
          "source_system": {
            "type": "string",
            "const": "feedbunk3000"
          },
          "organisation_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "site_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "batch_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "extracted_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "records": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/RationAssignmentRecord"
            }
          }
        },
        "required": [
          "schema_version",
          "provider",
          "source_system",
          "organisation_id",
          "site_id",
          "batch_id",
          "extracted_at",
          "records"
        ],
        "additionalProperties": false,
        "description": "Whole-batch structural and business validation. The token determines allowed scope. See the written contract for cross-field rules."
      },
      "FeedQuantityBatch": {
        "type": "object",
        "properties": {
          "schema_version": {
            "type": "string",
            "const": "1.0"
          },
          "provider": {
            "type": "string",
            "const": "elynx"
          },
          "source_system": {
            "type": "string",
            "const": "feedbunk3000"
          },
          "organisation_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "site_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "batch_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "extracted_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "records": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/FeedQuantityRecord"
            }
          }
        },
        "required": [
          "schema_version",
          "provider",
          "source_system",
          "organisation_id",
          "site_id",
          "batch_id",
          "extracted_at",
          "records"
        ],
        "additionalProperties": false,
        "description": "Whole-batch structural and business validation. The token determines allowed scope. See the written contract for cross-field rules."
      },
      "MovementBatch": {
        "type": "object",
        "properties": {
          "schema_version": {
            "type": "string",
            "const": "1.0"
          },
          "provider": {
            "type": "string",
            "const": "elynx"
          },
          "source_system": {
            "type": "string",
            "const": "stockaid"
          },
          "organisation_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "site_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "batch_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "extracted_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "extract_start": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "extract_end": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "records": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/MovementRecord"
            }
          }
        },
        "required": [
          "schema_version",
          "provider",
          "source_system",
          "organisation_id",
          "site_id",
          "batch_id",
          "extracted_at",
          "extract_start",
          "extract_end",
          "records"
        ],
        "additionalProperties": false,
        "description": "Whole-batch structural and business validation. The token determines allowed scope. See the written contract for cross-field rules."
      },
      "RecordError": {
        "type": "object",
        "properties": {
          "record_index": {
            "type": "integer",
            "minimum": 0
          },
          "record_id": {
            "type": "string"
          },
          "field": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "additionalProperties": false
      },
      "Error": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecordError"
            }
          }
        },
        "required": [
          "code",
          "request_id"
        ],
        "additionalProperties": false
      },
      "BatchReceipt": {
        "type": "object",
        "properties": {
          "batch_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "stored",
              "failed"
            ]
          },
          "received_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
            "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
          },
          "record_count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500
          },
          "status_url": {
            "type": "string",
            "pattern": "^/v1/integrations/elynx/batches/[^/]+$"
          },
          "stored_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$",
                "description": "Exact RFC3339 instant with offset; preserve up to nanosecond precision."
              },
              {
                "type": "null"
              }
            ]
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecordError"
            }
          },
          "receipt_id": {
            "type": "string",
            "format": "uuid"
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "synthetic": {
            "type": "boolean",
            "const": true
          }
        },
        "required": [
          "batch_id",
          "status",
          "received_at",
          "record_count",
          "status_url",
          "receipt_id",
          "stored_at",
          "error_code",
          "synthetic"
        ],
        "additionalProperties": false,
        "description": "stored means fully persisted to Influx and published. queued means durable inbox acceptance only. failed remains accepted work requiring controlled recovery."
      },
      "StoredWebhook": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "event",
          "event_id",
          "batch_id",
          "organisation_id",
          "site_id",
          "stream",
          "synthetic",
          "records"
        ],
        "properties": {
          "event": {
            "const": "batch.stored"
          },
          "event_id": {
            "type": "string",
            "format": "uuid"
          },
          "batch_id": {
            "type": "string"
          },
          "organisation_id": {
            "type": "string"
          },
          "site_id": {
            "type": "string"
          },
          "stream": {
            "enum": [
              "head-counts",
              "ration-assignments",
              "feed-quantities",
              "movements"
            ]
          },
          "synthetic": {
            "const": true
          },
          "records": {
            "type": "array",
            "maxItems": 500,
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/HeadCountRecord"
                },
                {
                  "$ref": "#/components/schemas/RationAssignmentRecord"
                },
                {
                  "$ref": "#/components/schemas/FeedQuantityRecord"
                },
                {
                  "$ref": "#/components/schemas/MovementRecord"
                }
              ]
            }
          }
        }
      },
      "AuthenticationResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "authenticated",
          "client",
          "credential",
          "integration_enabled",
          "capacity"
        ],
        "properties": {
          "authenticated": {
            "type": "boolean",
            "const": true
          },
          "client": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "organisation_id",
              "site_id"
            ],
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "organisation_id": {
                "type": "string"
              },
              "site_id": {
                "type": "string"
              }
            }
          },
          "credential": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "prefix",
              "permission",
              "expires_at"
            ],
            "properties": {
              "prefix": {
                "type": "string",
                "description": "Public token identifier; not the secret."
              },
              "permission": {
                "type": "string",
                "enum": [
                  "read",
                  "push"
                ]
              },
              "expires_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "integration_enabled": {
            "type": "boolean",
            "description": "Whether a client integration is enabled. Individual endpoints may have further requirements."
          },
          "capacity": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "plan",
              "push_calls_per_minute",
              "outgoing_calls_per_minute",
              "control_calls_per_minute",
              "calls_per_utc_day"
            ],
            "properties": {
              "plan": {
                "type": "string"
              },
              "push_calls_per_minute": {
                "type": "integer",
                "minimum": 1
              },
              "outgoing_calls_per_minute": {
                "type": "integer",
                "minimum": 1
              },
              "control_calls_per_minute": {
                "type": "integer",
                "minimum": 1
              },
              "calls_per_utc_day": {
                "type": "integer",
                "minimum": 1
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "batchStored": {
      "post": {
        "summary": "Deliver a verified stored synthetic batch",
        "description": "Only the approved Mac HTTPS target is currently enabled. HMAC-SHA256 over timestamp + dot + delivery UUID + dot + exact body bytes; v1=hex digest. Stable event ID and frozen payload on retries, up to five attempts. Receiver must validate TLS, signature, timestamp within five minutes and durably deduplicate event IDs/hashes, applying record revisions/voids independent of arrival order. Redirects are not followed. See the operating guide.",
        "security": [],
        "parameters": [
          {
            "name": "X-Origo-Delivery-ID",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Origo-Key-ID",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Origo-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Origo-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoredWebhook"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Receiver durably accepted/deduplicated the event."
          },
          "403": {
            "description": "Receiver rejected signature/timestamp/key."
          },
          "409": {
            "description": "Receiver found an event ID with different payload bytes."
          }
        },
        "tags": [
          "Webhooks"
        ]
      }
    }
  },
  "security": [
    {
      "PartnerBearer": []
    }
  ]
}
