# IPS Controllers Partner API > REST API for third-party applications to read IPS pool and spa chemical controllers (pH, ORP, temperature, free chlorine, alarms, settings) on behalf of an IPS customer who approved access, and to receive signed webhook events. OAuth 2.1 authorization code with PKCE; every token acts for one customer's grant. Base URL: https://integrations.ipscontrollers.com/v2 ## Contract - [OpenAPI 3.1 (YAML)](https://integrations.ipscontrollers.com/openapi.yaml): authoritative; every operation, schema, error, event payload and settings limit - [OpenAPI 3.1 (JSON)](https://integrations.ipscontrollers.com/openapi.json): the same document as JSON - [OAuth server metadata](https://integrations.ipscontrollers.com/.well-known/oauth-authorization-server) ## Documentation - [Integration Manual (PDF)](https://integrations.ipscontrollers.com/docs/integration-manual.pdf): onboarding, OAuth walk-through, permissions, settings limits, webhooks and signature verification, go-live checklist - [Developer guide (Markdown)](https://integrations.ipscontrollers.com/docs/developer-guide.md) ## Rules 1. Authorization code flow with PKCE (S256) and `state`. No client-credentials flow. Access tokens last 15 minutes; refresh tokens rotate on every use, and reusing an old one revokes the customer's grant. 2. Call `GET /me` first; use the controller `id` values it returns (`ctl_...`). Never use MAC addresses or names as identifiers. 3. Permissions are checked live on every call. 401: refresh the token or reconnect the customer. 403: not allowed on this controller now. 404: not shared (or no longer shared). Do not retry 403 or 404. 4. Settings changes (`PATCH /controllers/{id}/settings`) are asynchronous: 202 with `change_id`, then a `settings.applied` or `settings.failed` webhook, or poll `GET /settings-changes/{change_id}`. Send only changed fields within the documented limits; 422 lists failing fields. These controllers dose pool chemicals: never change settings in a loop or retry automatically. 5. Pagination: pass `next_cursor` as `cursor` until it is null. Readings need `from` and `to`, at most 31 days apart. 6. Limits: 600 requests/minute per client, 120/minute per customer grant. On 429 wait `Retry-After` seconds. 7. Webhooks follow Standard Webhooks (HMAC-SHA256, headers `webhook-id`, `webhook-timestamp`, `webhook-signature`). Verify every delivery and reject timestamps more than 5 minutes off. Deduplicate on the event `id`. Answer 2xx within 10 seconds. 8. Within v2, new fields, alarm types and event types may be added. Ignore what you do not recognise.