API-first SaaS: designing the public API before the UI
What does API first SaaS mean, and why design the public API before the UI?
API-first means the UI is a client of the same API partners will use, which keeps behaviour consistent and enables an ecosystem. Design the resources, permissions, versioning and webhooks first, build your own front end against them, and every feature ships to the browser and to integrators on the same day.
API first SaaS is a product where the public API is designed before the screens, and where the company's own web and mobile apps are simply the first clients of that API. Nothing the UI can do is unavailable to a partner, and nothing a partner can do bypasses the rules the UI follows. The payoff is consistency, a smaller codebase and the ability to become a platform rather than a tool. This article covers how we approach it in SaaS development work: what to design first, the decisions that are hard to reverse, how webhooks and developer experience fit, and what it costs to do properly.
What API-first means in practice
Most SaaS products grow a public API late, as a thin wrapper over whatever the UI happened to need. The result is two ways of doing everything: an internal path with all the business rules and an external path that misses half of them. Bugs appear only for integrators, permissions differ between the two, and the API team spends its time catching up.
API-first reverses the order. The team writes the resource model and the contract, reviews it as a product decision, and builds the UI as a consumer. Internal endpoints that the UI needs and partners do not are allowed, but they follow the same authentication, permissions and versioning, so promoting one to public is a documentation change rather than a rebuild.
The decisions to make before the first screen
| Decision | Options | Our default | Why it is hard to reverse |
|---|---|---|---|
| Resource model | Mirror the database, or model the domain | Model the domain; hide storage | Partners build against the names you publish |
| Identifiers | Sequential integers, UUIDs, prefixed opaque IDs | Prefixed opaque IDs | Integers leak volume and invite enumeration |
| Authentication | API keys, OAuth 2.0, both | Keys for server-to-server, OAuth for user-delegated access | Changing auth breaks every integration at once |
| Versioning | URL path, header, date-based | Date-based version pinned per key | A version scheme is a promise about the future |
| Pagination | Offset, cursor | Cursor everywhere | Offset breaks under inserts and is slow at depth |
| Errors | Ad hoc, structured with codes | Structured problem objects with stable codes | Integrators write logic against error codes |
| Rate limits | None, global, per key and tenant | Per key and tenant with headers | Limits added later feel like a breaking change |
Modelling resources around the domain
The API should read like the business, not like the schema. A customer-support product exposes conversations, messages and assignments; it does not expose the join table that links agents to queues. Each resource has a clear owner, a lifecycle and a set of actions that are verbs, not field updates. State transitions with side effects, such as closing a conversation or issuing a refund, get explicit endpoints so the rule runs in one place.
Permissions belong to the resource model. Every request carries a principal, and every resource answers the same question: can this principal do this action on this object in this tenant. The UI asks the same question by calling the same API, which is why the permission model in copilot actions through your existing API works for AI agents as well: they are just another client.
Idempotency and the write path
Every write endpoint that creates or charges should accept an idempotency key so a retried request cannot create a duplicate. Store the key with the result for a defined window and return the original response on replay. Partners' networks are not yours; they will retry.
Versioning without breaking anyone
Add fields freely, never remove or rename them silently, and never change the meaning of an existing value. When a change must break, introduce a new version and pin each API key to the version it was created under, so existing integrations continue unchanged while new ones get the improvement. Publish a deprecation timeline and email the owners of affected keys well before the date. Version drift is tracked in the API gateway, so you know exactly who is still on the old behaviour.
Webhooks: the other half of the API
Partners want to know when things change without polling. Webhooks deliver events to a URL the partner registers: signed with a shared secret, retried with backoff on failure, and replayable from a dashboard. Each event carries the resource identifier and a version, so the receiver can fetch the current state rather than trusting a payload that may have been superseded by the time it arrives.
Design the event catalogue with the same care as the resources. Name events by resource and action, include the tenant, and guarantee at-least-once delivery so consumers build idempotent handlers. The same events power your own product's real-time UI and any AI agents that react to changes, which is one more reason the internal and external paths should be the same path.
Developer experience is product work
An API is only a platform if someone can integrate without emailing you. Generate the reference from the contract so it cannot drift. Provide a sandbox tenant with sample data, keys that can be rotated from the admin, request logs the developer can inspect, and a quickstart that reaches a working call in a few minutes. The OpenAPI specification is the primary reference for describing the contract, and it lets client libraries, docs and validation be generated from one file.
Treat the developer portal like onboarding for a second kind of user, with the same funnel discipline described in SaaS onboarding that converts: time to first successful call is the metric.
Where AI features fit an API-first product
An API-first product is the easiest kind to add AI to, because the assistant, agent or copilot is one more client with its own principal and permissions. It reads through the same endpoints, writes through the same gated actions and emits the same events. There is no shadow path for the model to take. That is the design assumption behind the SaaS copilot service, and it is why products built API-first ship their first AI feature faster than products that have to build an internal API for the model to use.
A worked example
A logistics SaaS had a web app used by dispatchers and a growing list of enterprise shippers who wanted to push orders from their own systems. The existing API had been written per request: each integration got the endpoints it asked for, with rules that differed from the web app. Support spent much of its time explaining why an order created by API behaved differently from one created on screen.
The rebuild started with the contract. Orders, stops, drivers and proof-of-delivery became the published resources with explicit actions for assignment and completion. The dispatcher web app and the driver mobile app were rebuilt as clients of that contract, and the shipper integrations were migrated to it under a versioning scheme with pinned keys. Webhooks replaced the polling the largest shipper had been doing every minute. The dispatch platform and driver app case study describes a similar platform; the API layer made the later dispatch automation a matter of adding one more client.
Team and timeline
An API-first build is a backend lead who owns the contract, two backend engineers, a front-end engineer building the UI as a client, a technical writer or engineer for the developer portal, and a QA engineer running contract tests. Your side names an API owner who can make product decisions about resources and versioning within a day.
For a new product the contract-first approach adds design time in the first two weeks and removes it later. It fits a six-week Launch 6 at $26,500–45,500 (from ₹17,60,000) for a focused product, or SaaS development from $31,500 (₹20.8L) for a larger one. Retrofitting a public API onto an existing product, including webhooks and the portal, is usually six to ten weeks under the API development and integrations service from $7,000 (₹4.4L) upward depending on the surface area. Running the API, its gateway and deprecations is part of a Care Plan; see the pricing page.
Before you start: a checklist
- List the resources and actions in business language before any endpoint is written
- Decide identifiers, authentication, versioning and pagination in one document
- Define the permission question every endpoint must answer
- Add idempotency keys to every write that creates or charges
- Design the webhook event catalogue alongside the resources
- Choose a contract format and generate the reference from it
- Plan the sandbox tenant and the quickstart to first call
- Name the API owner and the deprecation policy before launch
Glossary
- Contract: the machine-readable description of the API that docs, clients and tests are generated from
- Principal: the authenticated identity making a request, whether user, key or agent
- Idempotency key: a client-supplied token that makes a retried write safe
- Cursor pagination: paging by an opaque pointer to the last item rather than a numeric offset
- Webhook: an HTTP callback the platform sends to a partner's URL when an event occurs
- Pinned version: the API behaviour an individual key is locked to until its owner upgrades
Related reading
Continue with adding an API layer to a legacy monolith for the retrofit case, MCP explained for how agents consume an API like this, and the SaaS industry page.
Design the contract, build your own UI against it, and the ecosystem follows without a second codebase.
Frequently asked questions
Does API-first slow down the first release?
▾
It moves a week or two of design to the start and removes far more later. Building the UI as a client of a stable contract means fewer duplicated rules, fewer integration-only bugs and a public API that exists on launch day.
Should internal endpoints be public?
▾
Not necessarily, but they should follow the same authentication, permissions and versioning as the public ones. Promoting an internal endpoint then becomes a documentation decision instead of a rebuild.
How do we version a public API safely?
▾
Add fields freely, never remove or repurpose them silently, pin each API key to the version it was created under, and publish a deprecation timeline with direct notice to affected key owners.