Before designing an endpoint, classify its nature. Only then choose its location.
The Silent Problem
There are many articles explaining how to write a REST API. Here, instead, we will focus on a prior question: how to decide where an endpoint should live before writing it?
When that criterion does not exist, the placement of each new endpoint ends up depending on intuition, precedent, or negotiation among developers. The result is usually an API tree that works at first, but becomes increasingly difficult to navigate and maintain.
The symptoms are well known:
- Nearly identical endpoints, created by different teams.
- Frontends that must assemble multiple calls for a single screen.
- URLs that reflect the org chart rather than the organization's core activity.
- Implicit conventions known only to those who have been on the project for years.
There is usually no grand architectural mistake. There are hundreds of small, inconsistent decisions.
The Central Idea
This article proposes a pre-design classification criterion. The idea is simple: before thinking about the URL, you must understand what type of need you are solving.
The thesis that runs through the entire text is:
Before designing an endpoint, classify its nature. Only then decide where it should live.
This is not about naming conventions or yet another REST discussion. It is about a decision framework that can be applied regardless of architectural style.
Six Possible Classifications
The framework establishes six categories. Each one responds to a different endpoint nature.
| Classification | Suggested Location |
|---|---|
| Core Domain | /{domain}/{resource} |
| View | /views/{name} |
| Integration |
/integrations/external/... and /integrations/webhooks/...
|
| Process | /processes/{name} |
| Configuration | /configuration/{table} |
| System | /system/{operation} |
As a general rule, when a requirement seems to fit more than one classification, it is best to prioritize the most specific and restrictive one. These six categories are the mechanism through which we solve the lack-of-criterion problem.
Core Domains: The Source of Truth
Core Domains represent the source of truth for the main activity. They contain atomic operations on their fundamental entities. They usually correspond to the central concepts managed by the organization: customers, orders, products, accounts, etc.
Example:
GET /api/v1/sales/orders/{id}
POST /api/v1/sales/orders
Here the operations are atomic, affect a single entity, and respect the principle that the HTTP method defines the action.
Views: Decouple the Consumer
Views do not exist solely to simplify queries. Their most valuable purpose is to decouple the consumer from the internal structure of the core domains.
A screen, a report, or an external system should not need to know how the core domains are organized internally. Views act as a decoupling layer: if tomorrow a domain is split or reorganized, the consumer can continue using the same View while the backend adapts its implementation.
Example:
GET /api/v1/views/sales-summary-by-customer
That summary may combine data from orders, payments, and shipments. The frontend receives exactly what it needs, without assembling calls or knowing the internal structure.
Processes: When the Goal Is to Execute, Not to Operate on an Entity
The Processes category groups endpoints whose nature is not to operate on a core domain entity, but to execute a coordinated sequence of actions. This may include:
- A month-end close
- A bank reconciliation
- A reversal or compensation of a complex operation
- A balance recalculation
- A settlement
- A registry synchronization
- A heavy batch process
- Any other routine that does not naturally fit into an entity CRUD
The name "processes" is intentionally broad. It does not assume massiveness or the number of entities affected. It only assumes that the endpoint exists to trigger a process, not to read or modify an atomic resource.
Examples:
POST /api/v1/processes/month-end-close
POST /api/v1/processes/reverse-operation/{id}
POST /api/v1/processes/bank-reconciliation
The question that defines this category is:
Does the endpoint exist to operate on a core domain entity, or to execute a process?
If the answer is "process," its place is /processes.
A Practical Example: Importing Customers
Suppose the following requirement appears:
"The system must allow importing customers from an Excel file."
Without a clear criterion, different teams might propose different locations for the endpoint:
POST /customers/import
POST /customers/import
POST /admin/customer-import
POST /batch/customers/import
All alternatives seem reasonable, but they respond to different intuitions. The result is that, over time, the API tree ends up growing inconsistently.
Applying this framework's decision flow:
- Does it interact with an external system? → No.
- Is it a read that composes multiple domains? → No.
- Is the goal to operate on a core domain entity? → No. It is not creating, modifying, or querying a customer atomically. The endpoint does not receive a representation of a Customer resource to operate on it atomically; it receives a binary file (Excel) that requires parsing, batch validations, and heavy coordination logic.
- Is the goal to execute a process? → Yes.
Therefore, the correct classification is Processes, and a coherent location would be:
POST /processes/customer-import
The important thing to observe is that the decision does not depend on the entity involved, but on the nature of the endpoint. Even though the process works on customers, the endpoint exists to trigger an import, not to operate on the customers resource.
This example illustrates the main goal of the framework: that two different architects, faced with the same requirement, reach the same decision by following the same reasoning, instead of depending on personal preferences or implicit conventions.
Integrations, Configuration, and System
-
External Integrations: to invoke external systems (active client). Example:
/integrations/external/salesforce/contacts. The contract is defined by the third party, and that determines both the location and the versioning policy: it is not the system's internal lifecycle that governs this interface. -
Webhooks: to receive notifications from external systems. Example:
/integrations/webhooks/paypal/payment-completed. The same logic applies in reverse: it is the third party that imposes the URL that must be exposed, which is why forcing it inside/api/v1/would be an empty convention. -
Configuration: for parametric or global tables. Example:
/configuration/taxes. These data exist as a separate lane because they are cross-cutting across multiple domains and do not have a clear domain owner; placing them inside any core domain would be an arbitrary decision. -
System: for technical operations of the API itself. Example:
/system/health. The inclusion rule is the absence of business semantics: if the endpoint makes sense to the business, it does not belong here.
Where Do Domains Come From? APQC and TM Forum
Defining high-level domains is usually a challenge. Inventing a proprietary taxonomy for each organization can lead to endless discussions and a tree that ends up reflecting the org chart rather than the actual activity.
To avoid this, the framework relies on mature and widely tested taxonomies:
- APQC PCF (Process Classification Framework): ideal for organizations with physical operations, logistics, manufacturing, and traditional corporate processes.
- TM Forum eTOM (enhanced Telecom Operations Map): designed for digital services organizations, telecommunications, platforms, and subscriptions.
Relying on these models is not a secondary detail. It is one of the greatest strengths of the approach, because it reduces naming discussions and makes the tree reflect operational reality.
By way of illustration, some examples of how these taxonomies translate into concrete URLs:
| Taxonomy | Examples of Domains and Resources |
|---|---|
| APQC |
/finance/chart-of-accounts/logistics/inventories/sales/opportunities
|
| TM Forum |
/customer/subscribers/operations/billing/resources/licenses
|
In the catalog available at you will find the detailed context trees based on APQC PCF and TM Forum eTOM, with a much more extensive list of domains and resources.
Rule: APQC and TM Forum are only used to structure the core domains. The other categories are cross-cutting and do not follow any business taxonomy, because their nature is not to operate on domain entities.
Decision Flow
To apply the classification in day-to-day work, it is useful to follow a practical decision flow:
Does it interact with an external system?
│
├── Yes → Is it an active call or a webhook?
│ ├── Active call → /integrations/external/{system}
│ └── Webhook → /integrations/webhooks/{source}/{event}
│
└── No → Is it a read that composes multiple domains?
│
├── Yes → /views/{name}
│
└── No → Is the goal to operate on a core domain entity?
│
├── Yes → /{domain}/{resource}
│
└── No → Is the goal to execute a process?
│
├── Yes → /processes/{name}
│
└── No → Is it global configuration?
│
├── Yes → /configuration/{table}
│
└── No → /system/{operation}
This tree is not dogma. It is a practical tool that helps maintain consistency when different teams or people design new endpoints.
Some Useful Conventions
Beyond classification, there are good practices worth adopting:
- Use
kebab-casethroughout the URL (example:processes/month-end-close). - Avoid verbs in the path; the action is defined by the HTTP method.
- Maintain uniform semantics for identifiers.
On the /api prefix
The /api prefix is used to separate programmatic interface endpoints from other server resources (static files, admin pages, etc.). It is not mandatory, but it is a widely adopted practice that allows applying specific policies (authentication, rate limiting, logging) over the entire API tree consistently. This article assumes its use, but each organization can decide whether to include it or not.
On Versioning
The /api/v1/ convention at the beginning of the URL is useful when all endpoints in a category share the same lifecycle. This naturally applies to Core Domains and Views.
However, other categories respond to contracts whose owner and evolution are external to the system. For example:
- A PayPal or Stripe webhook imposes the URL that must be exposed. It makes no sense to force it inside
/api/v1/. - An active integration with Salesforce may need its own versioning, for example
/integrations/external/salesforce/v2/..., independent of the global versioning.
General rule: the /api/v1/ prefix should not be applied mechanically to all categories. Each classification may define its versioning policy according to its nature. For integrations and webhooks, the versioning strategy derives from the contract that governs that interface, not necessarily from the global API versioning.
To Close
A good API architecture is not about finding a place for every endpoint. It is about having a criterion that makes that place obvious before writing it.
When two different architects reach the same decision by following the same reasoning, the tree stops depending on intuition and becomes part of the system's architecture. Discussions about where to place a new endpoint become fast, predictable, and grounded.
This work is intended as a starting point. Rather than establishing immutable rules, it seeks to propose a classification criterion that can be enriched by the community's experience and debate. No framework survives without evolution, and neither will this one.
Final note: the proposal of the six classifications and the decision flow is open. If your organization finds that an additional category or a variant works better, the fundamental principle —classify before designing— remains what really matters.
Top comments (0)