Frequently asked questions
Answers to the questions integration partners ask most, for rules that apply across the whole API. Rules that belong to one endpoint (booking rules, filters, field meanings) are on that endpoint's page in the reference.
Tokens
How often should we request a token?
Once per day. Exchange your client_id and client_secret at POST /oauth/token, then cache the access token and reuse it for its full expires_in (24 hours). Request a new one only when it has expired or a call returns 401. Never request a token per call or per sync run: the token endpoint is rate-limited separately from the API, and a client that requests tokens too often can be refused.
Can several servers share one token?
Yes. A token is tied to your client credentials, not to a machine. Share one cached token across all of your workers rather than having each request its own.
Where do the client credentials go?
Keep the client_secret on your servers only, like a password: never in a browser, a mobile app or source control. If it may have leaked, tell us and we will issue a new one.
Rate limits
What are the limits?
| Scope | Limit |
|---|---|
| Each API area (patients, appointments, invoices, …) | 100 requests per minute per client |
| Pricebooks | 250 requests per minute per client |
Limits are counted per OAuth client, separately for each API area, in one-minute windows.
How do we know how close we are?
Every response that passes authentication carries three headers:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed in the window |
X-RateLimit-Remaining | Requests left in the current window |
X-RateLimit-Reset | When the window resets, as a Unix time in seconds |
Over the limit, the API returns 429 with {"message":"Too many requests. Please try again later."} and a Retry-After header giving the seconds to wait.
How do we stay under the limit?
- Send
page_size=1000on list endpoints instead of the default 50. - Sync incrementally with each endpoint's change filter (for example
changed_at_starton appointments,changed_date_starton patients and invoices) instead of re-reading everything. - On a 429, wait for
Retry-Afterbefore sending more; don't retry at once.
Pagination
What is the largest page size?
1000 on every list endpoint, in staging and production alike. The default is 50. A larger value is refused with 400, and the whole request fails, so don't send more than 1000.
How do we know we have the last page?
Each list response has a pagination block with page, page_size, total_pages and total_count. Stop when page reaches total_pages, or when a page returns fewer rows than page_size.
Dates and time zones
What time zone are the timestamps in?
There are two kinds of endpoint:
| Endpoint | Timestamps |
|---|---|
| Schedule blocks, prescriptions, lab orders | Each time ships twice: store-local, and a _utc twin ending in Z. store_timezone gives the store's IANA time zone. |
| Every other endpoint | Store-local, with no zone: YYYY-MM-DDTHH:mm:ss. |
"Store-local" means the time zone of the store the record belongs to, so a practice with stores in several zones returns each record in its own store's time. Prefer the _utc fields wherever an endpoint has them.
Can we send UTC?
On appointment create and update, yes: a value ending in Z is converted to the store's local time, and a value with no zone is taken as store-local. Numeric offsets such as +05:00 are refused.
On filters, it depends on the endpoint, and each filter's description says which:
- Schedule blocks and lab orders accept
Zand convert it to the store's local time. - Every other filter takes store-local values only, and a
Zsuffix is refused with 400. Convert UTC to the store's local time before sending.
Is it safe to sync on change dates?
Yes, with a margin. Record audit times are stamped by more than one part of the system, so treat them as change markers rather than exact instants: start each incremental window a few hours before the end of the last one, and de-duplicate by the record's id.
Where do we find a store's time zone?
On the endpoints that return store_timezone (schedule blocks, prescriptions and lab orders). GET /stores does not return a time zone today.
Empty results
What happens when a search matches nothing?
Today it depends on the endpoint:
- Most list endpoints return 404 with a message such as "Patient not found" or "Order not found", and
"isOperational": truein the body. Treat that 404 on a list endpoint as an empty result. - Newer list endpoints (lab orders, prescriptions, inventory, eligibility documents) return 200 with an empty
dataarray.
All list endpoints will move to 200 with an empty data array. We will give partners at least five business days' notice before that change. Asking for one record by an id in the address (for example /invoices/{invoice_id}/lab) will still return 404 when it does not exist.
Errors
What does an error look like?
{"status":"ERROR","message":"Patient not found","error":{"statusCode":404,"isOperational":true}}
message says what went wrong; on a 400 it names the fields that failed. A few responses carry message alone: a 429, and a 403 for a missing scope (which adds required_scope and available_scopes).
Which errors should we retry?
| Status | Meaning | Retry? |
|---|---|---|
| 400 | The request is invalid; message says why | No: fix the request |
| 401 | Missing, expired or invalid token | Once, with a new token |
| 403 | Your client is not authorized for this practice, or lacks the endpoint's scope | No: ask for access |
| 404 | Not found (or, on most list endpoints today, nothing matched) | No |
| 429 | Over the rate limit | Yes, after Retry-After |
| 500 | Our error | Yes, with backoff |
| 502, 503, 504 | The gateway in front of the API could not get an answer in time | Yes, with backoff |
Can a retried create make a duplicate?
There are no idempotency keys. A create that timed out may still have succeeded, so look the record up before sending it again. Each create endpoint's page says how it handles a repeat: an appointment create refuses an exact duplicate with 400, and an eligibility document upload stores every copy.
Access
Which scope does an endpoint need?
Each endpoint's page lists its Required scope. A 403 for a missing scope names the scope it needs (required_scope) and the ones your token has (available_scopes).
How does a practice give us access?
An administrator at the practice submits a partner activation request naming your company. Allow three to five business days.
Is there a test practice?
Yes. Staging has its own credentials and a test practice with sample patients and appointments. Ask your Eye Cloud Pro contact for staging access, and use the Staging reference on this site to try requests against it.
