The multi-tenant API request lifecycle
An API request passes through a series of stages between arriving at the edge and returning a response. But why should you care?
Because none of those stages exist for their own sake. Each one is there to support something your business depends on: keeping every tenant's data inside its own boundary, giving consumers a contract they can build against, protecting the capacity you pay for, and giving your team what it needs to keep the API running. Get the lifecycle right, and those things hold by default. Leave it to individual routes and developers, and they hold only as long as nobody forgets.
This article walks through that lifecycle for a multi-tenant API and, for each stage, sets out what it does and who it serves. It is a guide to the checks and the reasoning behind them rather than an implementation manual. The principles hold whether enforcement lives in application code, a gateway, database policies or a platform that provides them by default.
A single principle shapes the ordering: invalidate a request as early as possible using the least expensive processing available. Cheap checks come before expensive ones, so long as each later check still has the information it needs to decide correctly. This protects the resources that should be serving legitimate tenants and keeps the rest of the lifecycle simpler, because each stage can rely on what the earlier ones have already established.
The lifecycle described here runs broadly in this order, though some stages can be swapped or combined depending on the architecture:
- Early request checks — protecting capacity before the application is involved.
- Identity — establishing who is calling, cheaply and without touching business data.
- Tenant context — determining which tenant, or set of tenants, the request applies to.
- Route authorisation — confirming the caller may use this operation at all.
- Structural validation of inputs — confirming inputs are well-formed before they are trusted.
- Object access checks — enforcing tenant boundaries on the specific data involved.
- Executing the API operation — the work the tenant asked for.
- Database constraints and error handling — the final integrity layer and how it reaches the consumer.
- Publishing events — letting other systems respond to change, within tenant boundaries.
- Response shaping — presenting data as a consistent contract.
Two further concerns run across the whole lifecycle rather than occupying a single position in it:
- Business-specific logic — standard and route-specific behaviour, which can execute before, during or after the operation.
- Logging and metrics — recording what happened at every stage, so the API can be understood and operated.
Early request checks
Before the request reaches most application logic, there are often a number of inexpensive checks that can reject clearly invalid or undesirable requests, which protects capacity that should be serving legitimate users.
These might include:
- TLS and connection handling
- Web Application Firewall rules
- IP or network restrictions
- rate limiting
- request-size limits
- supported HTTP methods
- basic route matching
- required headers
- content-type checks
- protection against obviously malformed requests
Importantly, these checks do not necessarily have to be implemented inside the API application itself.
A reverse proxy, API gateway, load balancer, CDN, Web Application Firewall or cloud platform may be better placed to perform some of them. For example, an API gateway might enforce request-size limits and rate limits before the request consumes any application resources at all.
Where these responsibilities live is an architectural decision. The important point is that requests which can be safely rejected before reaching expensive application processing should generally be rejected there.
Identity
Once these initial checks have passed, identity should normally be established before significant application processing or database access occurs.
The reason follows the same principle: if a caller cannot establish a valid identity, there is little value in spending CPU, memory or database resources processing the request further.
Internet-facing APIs should also assume that endpoints will eventually be scanned, probed and called using invalid or fabricated credentials, so authentication should be designed to reject these requests efficiently.
A common approach is for the caller to obtain an access token, often a JSON Web Token (JWT), from a trusted Identity Provider. The token is valid for a limited period and contains claims describing the authenticated identity and other relevant information.
Where asymmetric signing is used, the Identity Provider signs the token using its private key. The API can then verify the signature using the corresponding public key, generally without performing a database lookup or contacting the Identity Provider for every request.
Verification should include more than simply checking that the signature is valid. Depending on the system, the API should also verify claims such as the token's issuer, audience and expiry before accepting the identity.
Enterprise authentication can introduce additional layers. For example, an organisation may authenticate users through SAML federation, but that federation will commonly occur between the organisation and the Identity Provider. The API itself may still receive the same OAuth or OpenID Connect access token as it would for other callers.
The important architectural principle is that establishing identity should be fast, stateless where practical, and independent of the application's business data. Treating identity as a pluggable seam, so that the API can accept tokens from your own identity provider or a customer's, is one way of keeping that independence; an example is described in the identity integration architecture.
At this point we know who the caller is. We have not yet established whether that caller is allowed to perform the requested operation.
Tenant context
For systems using a separate schema per tenant or a separate database per tenant, determining tenant context is often one of the earliest important steps in the request lifecycle. However, when it is done is implementation-specific.
The reason is architectural: before the API can perform most data operations, it needs to know which schema, database or connection configuration should be used for the request.
Tenant context can be supplied or derived in several ways, including:
- hostname or subdomain
- URL path or query parameters
- request headers
- claims within an authenticated token
- information derived from an upstream gateway or proxy
A common pattern is for a tenant-specific domain or subdomain to be resolved by a reverse proxy or API gateway, which then passes a normalised tenant identifier to the API in a header or route parameter.
For systems using a shared database and shared schema, tenant handling can be different.
The request may not need to select a single tenant at the beginning of the lifecycle. A user may legitimately have access to multiple tenants, and the API may instead derive the allowed tenant scope from that user's permissions and apply it when querying or modifying data.
In that model, the important concept is not necessarily a single tenant context, but the set of tenants or records that the caller is permitted to access.
The choice between database per tenant, schema per tenant and shared tables affects far more than this one stage. I've written separately about choosing a multi-tenant database architecture as a business decision.
Route authorisation
Once identity has been established, the API should determine whether the caller is permitted to invoke the requested operation.
For example:
Can user Jonny Bgood invoke the operation update locations?
This is different from checking whether the user may update a particular location record. At this stage, the API is primarily determining whether the caller has permission to use the route or operation at all.
If this information is available in the caller's token or can be resolved cheaply, it provides another opportunity to reject an invalid request before performing more expensive processing.
Route authorisation may also depend on tenant context. For example, a user may be allowed to update locations for one organisation but only view locations for another.
In smaller systems, route permissions may be maintained directly by the API. In larger enterprise systems, they are often managed by a separate Identity and Access Management or Access Management system.
These systems commonly store relationships such as:
- which users or machine identities exist
- which APIs they can access
- which operations they may perform
- which tenants or organisations those permissions apply to
- which roles or profiles group those permissions together
Roles are generally a convenient way of grouping permissions rather than assigning every permission individually. In relational systems, the organisational structure in the data itself (organisation, location, department) is often the most natural thing to hang those permissions from; see how access can follow your data's structure.
If route authorisation requires a network request to an external access-management service, this can become significant in the request lifecycle. Frequently used permission information may therefore be cached by the API, provided that the cache design accounts for permission changes and revocation requirements.
The objective remains the same: determine as cheaply as practical whether the caller has any right to continue processing the request.
Structural validation of inputs
All inputs that can influence the behaviour of the request should be validated before they are trusted.
Structural validation of inputs has three main benefits: it minimises API resource usage, protects data integrity, and reduces unexpected errors.
Depending on the API, these inputs may include:
- path parameters
- query parameters
- request headers
- request body content
- values derived from routing or tenant context
At this stage, validation should normally focus on checks that can be performed without accessing application data. These include:
- required fields
- data types
- string lengths
- numeric ranges
- enumerated values
- formats
- structural relationships within the request body
These checks are generally computationally inexpensive and allow malformed requests to be rejected before database queries or business logic are performed.
Failing at this stage will commonly result in a 400 Bad Request, accompanied by a useful JSON response that identifies which input failed validation and why.
Validation also protects the integrity of the data entering the system. Invalid or unexpected input should be rejected at the API boundary rather than relying on downstream systems to handle it safely.
There is another important operational consideration: the validation rules implemented by the API should remain consistent with the API's published contract.
If OpenAPI documentation states that a field is required, accepts a particular enum or has a specific maximum length, the API should enforce those same rules. The same applies to documentation intended for LLMs or other automated consumers.
Maintaining separate definitions for runtime validation, OpenAPI documentation and machine-readable documentation creates a risk that those definitions will drift apart over time. Generating the documentation from the same executable definitions that run the API removes most of that risk, and makes it possible to show each consumer only the routes they are permitted to call; see permission-aware documentation.
Object access checks
Object access checks exist to support tenant boundaries and help to ensure the integrity of data.
Object access checks are particularly important in a shared database, shared schema tenant model.
At a high level, there are three main checks that need to occur:
- Tenant consistency: ensure that the object being operated on and any related objects referenced by the request belong to the same tenant.
- Operation-specific access: ensure that the caller has permission to perform the requested operation on that object for that tenant.
- Tenant immutability on update: ensure that an ordinary update cannot change an object's tenant ownership.
These checks apply not only to the primary object being created, updated or deleted, but also to foreign-key relationships and other referenced objects involved in the operation.
Tenant consistency
The API needs to ensure that all objects participating in an operation belong to the same tenant context.
For example, if an invoice line is being assigned to an invoice, both the invoice line and the invoice must belong to the same tenant. A user should not be able to attach an invoice line belonging to Company A to an invoice belonging to Company B.
The same principle applies to any foreign-key relationship that can influence tenant ownership or allow data to cross tenant boundaries.
For write operations, these checks should normally occur inside the same database transaction as the resulting mutation.
Where the consistency check depends on existing foreign-key records, the relevant rows should be locked where necessary so that they cannot be changed or deleted by another transaction before the operation completes.
This means the API is not only checking that the relationship is valid at one instant in time. It is protecting the state used to make that decision for the duration of the operation.
This becomes especially important with bulk operations, where every affected object and every relevant relationship needs to remain within the permitted tenant scope.
These checks are independent of the technology used to enforce them. They may be implemented in application code, generated queries, database policies such as Row-Level Security, stored procedures, or a combination of approaches. What matters is that the same tenant and access invariants are enforced consistently for every operation.
Operation-specific access
The API must also confirm that the caller has permission to perform the specific operation for the tenant involved.
Access to a tenant does not automatically imply the same permissions for every operation.
For example, a user may have permission to update invoices for Tenant A while having read-only access to invoices for Tenant B.
Having access to both tenants must not result in the update permission for Tenant A being applied to Tenant B.
The authorisation therefore needs to consider at least:
- the caller
- the tenant
- the object or object type
- the operation being performed
This is distinct from route authorisation. Route authorisation establishes that the caller may use the operation in principle. This check establishes that they may perform it against this tenant and this data.
Tenant immutability on update
Once an object belongs to a tenant, an ordinary update should generally not be able to change that tenancy.
For example, changing an invoice's tenant identifier from Company A to Company B should not be treated as a normal field update.
Allowing tenant ownership to change casually can bypass the other access rules and can also create invalid relationships with child records or foreign keys.
If transferring an object between tenants is a valid business requirement, it should normally be implemented as an explicit operation with its own authorisation, validation and consistency rules.
Treating tenant ownership as immutable during ordinary updates makes the security model considerably easier to reason about.
Taken together, these checks ensure that an operation:
- does not create relationships across tenant boundaries
- is being performed by a caller with the correct permission for that tenant
- cannot silently move an existing object into another tenant
Ideally, these protections should be consistently applied across every model and route, enabled by default rather than relying on individual developers to remember to implement them. One way to achieve that is to derive tenant isolation from how the data relates, so it is applied the same way on every route; that approach is set out in the tenant isolation architecture.
Executing the API operation
This is the core function of the API: the operation the tenant requested, performed within the rules the API owner has defined.
By this stage, the request should have passed authentication, route authorisation and structural validation, and the appropriate tenant and object-access rules should be ready to be applied as part of the database operation.
Read operations
For a Get One, List or Search operation, the API should generally not retrieve objects first and then perform a separate access check.
Instead, the caller's permitted data scope should form part of the query itself.
The database query should be structured so that records outside the caller's permitted tenants or object scope cannot be returned in the first place.
Conceptually, rather than:
Find invoice 123, then determine whether the caller is allowed to see it.
the query should behave more like:
Find invoice 123 within the set of invoices this caller is permitted to see.
The same applies to List and Search operations. Tenant and permission predicates should be incorporated into the filtering and joins used to retrieve the data.
This is safer, more efficient and prevents unauthorised records from being returned to application code at all.
Larger systems may also distinguish between the primary database and read replicas. Read replicas can reduce load on the primary database, but replication lag needs to be considered. A request that must immediately observe a previous write may need to read from the primary rather than a replica. How read and write traffic are separated, and what is tuned in software before scaling infrastructure, is covered in how the API scales.
Write operations
For Create, Update, Delete, bulk operations and UPSERTs, the relevant object-access checks and the resulting data changes should generally be performed within the same database transaction.
Where access decisions depend on existing records or relationships, those rows should be locked where necessary so that the state used to authorise the operation cannot change before the mutation completes.
Depending on the implementation, access rules may be incorporated directly into the write query or evaluated through queries performed earlier within the same transaction.
Concurrency also needs to be considered. Deadlocks, lock timeouts, serialisation failures or other transient database conditions may cause a transaction to fail. Depending on the operation and database behaviour, some of these failures may be suitable for controlled retries.
If the operation fails, the transaction should roll back so that a partially completed change cannot be committed.
Database constraints and error handling
The database remains an important final layer for maintaining data integrity, even when substantial validation has already happened in the API.
A correctly designed write lifecycle should already have checked and, where necessary, locked foreign-key objects used for access control or tenancy. Therefore, foreign-key failures caused simply by a referenced row disappearing between the access check and the write should normally have been prevented by the transaction and locking strategy.
Database constraints are nevertheless still important.
Failures that can still occur include:
- unique constraint violations
- check constraint violations
- not-null violations
- foreign-key violations involving relationships not already protected by the access-checking process
- deadlocks and lock timeouts
- serialisation or other concurrency failures
- errors deliberately raised by database triggers or stored procedures
A unique constraint is a good example of why application-side validation is not enough. The API may check that a value is currently unique, but another transaction could create the same value before the operation commits. The database constraint remains the authoritative protection against that race.
These failures should be handled deliberately rather than treated as generic internal errors.
The API should translate expected database failures into responses that are meaningful to the consumer.
Exposing a raw database constraint name, SQL error or internal trigger message is generally not useful and may reveal implementation details. Instead, the API should convert expected database failures into its normal, stable error structure.
Where database triggers or stored procedures implement business rules, they may deliberately reject an operation and provide information that needs to reach the API consumer. That information should be mapped into the API's standard error response rather than simply passing the raw database error through.
This highlights an important principle: API validation and database constraints complement each other.
The API provides early and useful feedback while avoiding unnecessary database work. The database provides the final integrity guarantees, particularly where concurrent operations are involved.
Error handling is therefore part of the API contract. Consumers should be able to distinguish consistently between validation failures, access failures, conflicts, missing resources and unexpected server errors.
Publishing events
In today's interconnected systems, simply being able to create, modify or retrieve data through an API is often not enough. Other applications frequently need to know when something has changed so they can respond.
For example, when a restaurant acknowledges an order, the customer may expect to see immediately that the order has entered the preparation queue. Similarly, when several users are editing the same document, each user may need to know when another user has made a change.
APIs can support these scenarios by publishing events when significant changes occur.
In a multi-tenant system, event delivery also needs to respect tenancy and permissions. A subscriber should only receive events for tenants and data that it is authorised to access. One approach is to make event topics permissioned resources under the same access model as the API itself, so what a consumer may see through the API is exactly what they may see through events; see how events fit the architecture.
Consumers may also need to distinguish between changes they initiated themselves and changes initiated elsewhere. A client may choose to ignore an event caused by its own request, while another device belonging to the same user may still need to respond to it.
Useful event metadata commonly includes:
- the user, service or process that initiated the change
- the model or resource type affected
- the tenant associated with the event
- the identifier of the affected object
- the server request identifier associated with the change
- a client or user-agent request identifier, where one was supplied
- the API or service that generated the event
- a URL or resource identifier that can be used to retrieve the current representation of the object
Additional information may be required depending on the type of event.
For a delete event, for example, it can be useful to include enough of the deleted object's previous representation for consumers to identify what was removed, because the object can no longer be retrieved from the API afterwards.
For update and create events, it is not always necessary to include the complete object or every field that changed.
One reason is that event delivery may be asynchronous. By the time a consumer receives an event, the object may already have changed again. Where the consumer needs the current state, retrieving the object from the API ensures that it receives the latest representation and that normal access controls are applied.
There are, however, cases where an event should contain a snapshot, changed fields, or sufficient information for the consumer to act without making another API call. The appropriate event payload therefore depends on whether the event represents a notification that something changed or a durable record of the change itself.
Whatever approach is used, event publishing should preserve the same tenant and permission boundaries as the API that produced the event.
Finally, event publication needs to be considered separately from the database transaction itself.
An event should only be published for a change that has successfully committed. Publishing before commit creates the possibility that consumers are told about a change that is later rolled back.
At the same time, publishing to an external event system cannot usually be made part of the same atomic transaction as the application database. This means there is normally some possibility that the database commit succeeds but the event publication fails.
For that reason, event publication is often performed asynchronously and may occur after the API has already returned a successful response to the caller.
Where stronger delivery guarantees are required, additional mechanisms can be introduced to record and retry events that have not been successfully published. One common approach is a transactional outbox, where an event record is written in the same transaction as the business change.
This provides stronger delivery guarantees, but it is not free. In many systems the database is already the primary write bottleneck, and adding an additional database write for every event increases transaction and replication load. The appropriate design therefore depends on the importance of guaranteed event delivery, the expected write volume and the performance characteristics of the system.
There is no single correct event-delivery strategy. The important requirements are that events are not published for transactions that fail, that failures in event delivery are understood and handled deliberately, and that the guarantees provided to consumers are clearly defined.
Response shaping
The data returned by the database is not necessarily the data that should be returned directly to an API consumer. Data needs to be shaped to support consumers of the API so that they have a consistent, understandable and useful experience.
Database conventions and API conventions often differ. For example, database columns may use snake_case, while an API may expose fields using camelCase. Dates, numbers and currencies may also need to be formatted into a consistent representation.
Response shaping may also include:
- hiding internal or implementation-specific fields
- renaming fields
- expanding related objects
- including calculated or derived values
- combining values from related tables
- adding pagination information
- including request or correlation identifiers
- wrapping the result in a standard response structure
Some responses may also vary depending on the caller. Certain fields may only be available to particular users, roles or audiences. Where this is an access-control requirement rather than merely presentation logic, the underlying permission should be enforced consistently rather than relying only on the final response transformation to hide the data.
Whatever transformations are applied, they form part of the API contract and should be reflected accurately in OpenAPI and other documentation provided to consumers.
Consistency is especially important. Field naming, date formats, pagination metadata, error structures and other response conventions should behave the same way across the API wherever possible. A predictable API is significantly easier for consumers to integrate with and maintain.
Business-specific logic
Business-specific logic is shown here as one section for clarity, but in practice it can execute at several points throughout the request lifecycle.
Every application API contains some amount of business-specific behaviour.
Platforms and frameworks such as CustomAPIs and Supabase can blur the boundary between generic API behaviour and application logic because they provide standard CRUD operations based on the underlying database structure. Some behaviour can therefore be implemented consistently across many resources rather than being rewritten for every individual route. The two take quite different approaches to where tenancy and access rules live; I've compared them in Supabase vs CustomAPIs for multi-tenant APIs.
It is useful to divide this behaviour into two broad categories: standard business logic and request-specific business logic.
Standard business logic
Standard business logic is behaviour that should apply consistently across many resources or operations.
Examples might include creating audit records when data changes, applying standard timestamps, publishing standard change events, enforcing common lifecycle behaviour or applying common data transformations.
Some of this logic may only apply to particular operations. Auditing, for example, may apply to Create, Update and Delete operations while having little or no relevance to a simple read.
Where behaviour is expected to be consistent throughout an API, it should ideally be implemented by the framework or through reusable components rather than independently implemented on every route. Auditing is the clearest case: when it is part of the framework lifecycle, every operation leaves a reliable history without any route having to remember to write one; see the audit architecture.
Request-specific business logic
Some routes require behaviour that is unique to that particular operation.
Examples might include preventing an invoice from being modified after it has been finalised, calculating derived values before an order is created, validating whether a requested state transition is permitted, or triggering a particular integration after an operation succeeds.
This is also where semantic or business validation belongs.
Structural validation may tell us that a value is correctly formatted. Business validation determines whether that value actually makes sense in the current state of the system.
For example, structural validation may confirm that invoiceId is a valid UUID. Business validation may then determine whether the invoice exists, whether it is still open, or whether the requested operation is allowed in its current state.
Business-specific logic may therefore occur at different points in the request lifecycle. Some checks need to happen before the database operation, while other behaviour occurs after a successful change.
Where request-specific behaviour exists, it should be easy for developers to locate and understand. Scattering custom logic across controllers, middleware, database triggers and unrelated helper functions makes an API increasingly difficult to maintain.
The behaviour documented for API consumers should also remain consistent with the implementation.
OpenAPI documentation, LLM-oriented documentation and other API contracts should describe any non-standard inputs, constraints or behaviour associated with the route.
Keeping route configuration, custom behaviour and documentation close together, or deriving them from a common definition, reduces the risk of the implementation and documentation drifting apart over time.
Logging and metrics
Logging and metrics are not the same thing, but they are closely related and are often most useful when designed together.
Logging is primarily used to understand what happened during the operation of the API and to diagnose problems.
Metrics are structured measurements used to understand the behaviour and health of the API over time. They are useful for reporting, alerting, performance analysis and capacity planning.
A key requirement for both is being able to associate information with the request that produced it. Request or correlation identifiers are therefore extremely important, particularly where a request may pass through multiple services.
Timestamps should also be recorded consistently, and log and metric data should be structured so that it can be parsed, searched and analysed by other systems.
For request-level metrics, useful information may include:
- request identifier
- start and completion timestamps
- HTTP method
- route or path
- response status
- response time
- user agent
- source IP address where appropriate
- relevant query or URL parameters where safe to record
Recording both the start and completion of a request can also help identify requests that begin processing but do not complete as expected.
Other contextual information can be valuable for diagnostics, including:
- tenant identifier or identifiers
- user or service identity
- application or client identifier
This information may not belong in every metric, but it can be extremely useful when correlating logs and investigating incidents.
In multi-tenant systems it can also be useful to selectively increase logging for a particular tenant or customer while investigating an issue, rather than increasing logging globally.
Log levels
Log messages should be recorded at appropriate severity levels so that operational teams can distinguish routine information from events that require attention.
Typical levels include:
- Critical — failures that leave the application or a major part of the system unavailable or severely impaired.
- Error — failures that represent an unacceptable condition or normally require investigation.
- Warning — unusual conditions that may not require immediate action individually, but may indicate a developing problem when their frequency increases.
- Info — normal operational information that can help explain what occurred during a request or process.
- Debug — highly detailed diagnostic information primarily intended for development or temporary troubleshooting.
The exact meaning of these levels should be defined consistently across the application. A warning in one part of the system should not represent the same severity as a critical failure elsewhere.
Protecting sensitive information
Logs can easily become a security and privacy risk if too much request data is captured.
Sensitive information such as bearer tokens, API keys, passwords, session credentials and other authentication material should never be written to logs.
Care should also be taken with personal, confidential or commercially sensitive information. Logging request bodies, headers or query parameters indiscriminately can result in data being stored in systems that were never intended to hold it.
Logging should therefore be deliberate rather than simply capturing everything available.
Designing for operations
When designing logging and metrics, it is useful to start by asking what information will be required to operate the API effectively.
For example, a well-designed set of request metrics should make it possible to answer questions such as:
- Which routes are responding slowly?
- What are the response times for each method and route?
- Which response codes are being returned and where?
- Are particular tenants experiencing worse performance?
- Is the frequency of warnings or errors increasing?
- Which requests are taking significantly longer than normal?
- Are requests failing to complete?
Much of this reporting can be derived from a relatively small amount of consistently structured request data.
The goal is not to collect the maximum possible amount of information. It is to collect enough reliable and well-structured information to understand the health of the API, diagnose failures and maintain the level of service expected by its consumers.
Observability platforms still require design
There are numerous logging, monitoring and observability platforms that can collect, aggregate and analyse this information for you. These systems can remove a large amount of implementation work, but they do not remove the need to design what your API should record.
You still need to decide which identifiers should be captured, which dimensions matter for reporting, what constitutes an error or warning, which data is safe to store, and which operational questions you need to be able to answer.
Without that design work, it is easy to collect a large volume of telemetry while still being unable to diagnose the problems that actually matter.
What a good API lifecycle achieves
The request lifecycle of a multi-tenant API should be a deliberate and well-structured piece of engineering.
It should reject invalid requests as early and cheaply as possible, protect CPU, memory and database resources, enforce tenant and access boundaries consistently, remain aligned with its published documentation, and provide enough observability to understand how the API is behaving in production.
It should also present a predictable contract to consumers. Validation, errors, response structures, permissions and event behaviour should be consistent across the API rather than varying from route to route.
Most importantly, though, the lifecycle exists to support the business. The purpose of all of these mechanisms is not simply technical correctness, but to provide a secure, maintainable and reliable foundation on which business rules, integrations and products can evolve.
I'm Sam Tuckey, founder of CustomAPIs. Everything above is how CustomAPIs approaches the request lifecycle, with tenant boundaries, validation and access checks enforced by default rather than per route. It's a proven architecture designed to hold up as you grow.
If you would like to know more about the CustomAPIs framework you can read the tenant API architecture.
Sources
- RFC 7519 — JSON Web Token (JWT)
- RFC 6749 — The OAuth 2.0 Authorization Framework
- OpenID Connect Core 1.0
- RFC 9110 — HTTP Semantics, 400 Bad Request
- PostgreSQL — Row Security Policies
- PostgreSQL — Explicit Locking
- PostgreSQL — Transaction Isolation
- Supabase — Data API
Sources were checked in September 2026. Supabase is a trademark of Supabase Inc.; we are not affiliated with, endorsed by or a reseller of Supabase.