Browser-side JavaScript that called the Ditto API has, until this week, been blocked at the CORS check — no headers at all, every request denied. Customers building web apps on top of Ditto had to proxy every call through their own server. That's now optional rather than mandatory.
What's new
**CORS shipped on every public
/v1/*endpoint** except the billing surface (more on that below).Origin allowlist plus regex. Submit a request from an allowlisted origin and you get the right
Access-Control-Allow-Originheader echoed back; submit from a denied origin and the request is refused cleanly. Both literal allowlist entries and regex patterns are supported, so customers running on*.theircompany.comdon't need a per-subdomain entry.Preflight is unauthenticated.
OPTIONSreturns 200 OK without checking credentials, so the browser never has to authenticate twice. Cached for 24 hours viaMax-Age: 86400.Rate-limit headers exposed. The new CORS policy adds rate-limit and request-ID headers to the
Access-Control-Expose-Headerslist, so browser clients can read them.Standard methods covered. GET, POST, PUT, PATCH, DELETE, OPTIONS — same set the API serves on the server-side path.
One restriction
/v1/billing/* is deliberately excluded from the CORS allowlist. Billing endpoints are server-only — the browser should never be calling Stripe-related routes directly. If your customer-facing billing UI needs to call those endpoints, do it from your own server.
How to enable for your origin
If you need an origin added to the allowlist, drop us a line. The default allowlist covers FishDog, Ditto, and localhost for development; production customer origins are added on request.
---


