Unified API: shared contracts across service APIs
A unified API gives consumers one shared contract over several service APIs. Source-specific adapters translate requests, records and failures between that contract and each service’s interface. The shared shape reduces what consumers need to understand, but supported operations and field meanings still depend on the underlying services.
By Ai Flowi · Published
What makes the contract unified?
The consumer asks for a common resource, such as a person record, using agreed field names and meanings. An adapter for each source handles its particular schema. Consumers can then display those records using the same logic, while the adapter remains responsible for differences that cannot be translated safely.
Calling one endpoint is ordinary API integration. A workflow run endpoint exposes a workflow’s inputs and output. An action catalogue lists available operations. None establishes a shared cross-service record contract by itself. The distinction is semantic: renaming two fields to state does not make their meanings identical.
Two fictional schemas, one record contract
These are explicitly fictional, read-only records. Source A defines active as a boolean. Source B defines status as text: enabled and disabled are known values; other strings remain unknown to this adapter. The records describe separate people, with no identity-matching claim.
{"id":"A-17","full_name":"Mira Chen","active":true}
{"record_key":"B-42","given_name":"Jon","family_name":"Rao","status":"paused"}
Define three canonical fields. source_id holds the source label and unchanged identifier. display_name holds display text, never an identity key. state holds a normalized value, the original value and a presence flag. Keeping the raw state makes information loss visible.
Tables can be focused and scrolled horizontally on narrow screens.
| Canonical field | Source A | Source B | Boundary rule |
|---|---|---|---|
source_id | Source A label plus id | Source B label plus record_key | Retain the original string, including leading zeros. Reject a missing, null or blank identifier. |
display_name | Trim full_name | Trim available text name parts and join with a space | Return null if no nonblank text exists. Do not guess missing names or split A’s full name. |
state.normalized | True → active; false → inactive | enabled → active; disabled → inactive | Unrecognized present values → unknown. Explicit null or absent field → null. |
state.raw and state.present | Original active value and whether its key exists | Original status value and whether its key exists | Retain unfamiliar values verbatim. Raw null with present true differs from raw null with present false. |
The intended canonical records are shown below. B’s paused is preserved as unknown; interpreting it as inactive would add a business rule the fictional source has not supplied.
{
"source_id":{"source":"A","id":"A-17"},
"display_name":"Mira Chen",
"state":{"normalized":"active","raw":true,"present":true}
}
{
"source_id":{"source":"B","id":"B-42"},
"display_name":"Jon Rao",
"state":{"normalized":"unknown","raw":"paused","present":true}
}
If A supplies active:null, state is {"normalized":null,"raw":null,"present":true}. If A omits active, only present changes to false. Neither becomes inactive. Invalid name-field types require review rather than automatic text conversion.
A capability matrix prevents false equivalence
Assume the fictional services have the capabilities below. These are design assumptions for the example, not documented provider features. A shared API must publish its operation coverage alongside its record schema.
| Operation | Source A | Source B | Shared contract |
|---|---|---|---|
| Read person by ID | Supported | Supported | Supported with a source-qualified ID |
| List people | Page numbers | Opaque cursor | Requires separate continuation adapters |
| Disable person | Write supported | No write operation | Excluded from common operations |
| Read separate family name | Unavailable | Available | Excluded from the canonical record |
This is the common-denominator tradeoff. A read-only directory view can use both sources. A consumer that must disable every person cannot use this common contract: changing B’s local normalized state would not perform a remote write. Source-specific extensions may expose richer operations, but consumers using them must understand that source.
What adapters must keep source-specific
- Credential ownership. The API integration guide covers authentication for an individual connection. For this directory, use your own provider accounts and credentials for A and B, paying each provider separately. Associate each connection with its source label: permission to read A-17 says nothing about permission to read B-42. If B access is revoked, show B as unavailable rather than silently presenting A’s records as the whole directory.
- Unmappable fields. A full display name cannot reliably produce separate given and family names. Leave that capability out, or expose a documented source-specific extension. Do not fill the gap with inferred personal data.
- Pagination. A combined directory needs two independent positions: A’s next page and B’s next cursor. Finishing A does not finish B. Bind continuation to the requested source set so a value created for A alone cannot resume an A-plus-B listing. If one source fails midway, identify the unfinished portion; a single global page number hides that distinction.
- Error preservation. An unsupported request to disable B-42 and a denied read of B-42 need different outcomes: the first exceeds the advertised capabilities, while the second fails an allowed operation. Return the source label and a safe diagnostic code without exposing its credential or person payload. Do not substitute A-17 after a B lookup fails, even if their display names happen to match.
- Schema versions. Suppose B adds
suspendedwithout defining whether it means inactive or temporarily unavailable. Preserve it as unknown under this adapter version. Record a revised meaning before changing the mapping, and compare old and new canonical results for the same B fixture. Consumers may use normalized state to hide records; an adapter update can therefore change the directory even when every field name stays the same.
How this appears in AI Flowi Workflow
example configuration, not a recorded test
The catalogue offers building blocks for illustrating an adapter. These interface roles are documented in the catalogue; not production-tested by Ai Flowi as this two-source example. They do not establish a managed unified API service.
Use the API Request reference for the HTTP configuration of each source read. Decide which adapter to call from source_id.source before sending the lookup: A uses its original id, while B uses record_key. Never infer the source from an identifier’s spelling. Both could issue 0007; requesting A’s 0007 when the consumer selected B’s 0007 returns the wrong person even if the response is well-formed. Check that the returned identifier matches the requested source-qualified identity before constructing the shared record.
The JSON Operations reference covers the exported Rename Keys
, Select Keys
and JQ Expression
choices. For A, changing the label active to state leaves a boolean where this contract requires a structured value. For B, selecting only the familiar enabled and disabled values would discard the example’s paused evidence. Capture the original state and key presence before removing source fields, then apply the declared value mapping. Build B’s display name from its available name parts; leave A’s full name unsplit.
Consult JSON Object Builder for object assembly and Condition for branching configuration. For this contract, an assembled record must retain source_id as an object rather than a flattened ID string. Compare A and B records with the same literal ID to confirm they remain distinct. Also compare a present-null state with an absent state: both require a retained raw:null, but their present flags differ. Dropping null fields or converting the nested state to text fails this contract, regardless of whether the editor accepts the connection.
If a directory separates active people from others, specify three outcomes: active, inactive and unresolved. B’s paused belongs in unresolved with its raw value available for diagnosis. A’s false boolean belongs in inactive, while absent and explicit-null states remain unresolved with different presence flags. A branch that merely fails an active comparison cannot establish inactivity. Before using component outputs, compare the complete source_id and state on each resulting record against these expectations; the displayed examples define acceptance criteria, not recorded routing results.
The workflow API guide explains exposing a workflow run. Publishing such an endpoint would still leave you responsible for this shared schema, operation coverage and adapter maintenance.
Plans: Go from USD8/month billed yearly (USD10 monthly), Plus from USD16 (USD20 monthly), Pro 100 from USD80 (USD100 monthly), Pro 200 from USD160 (USD200 monthly) and Pro 500 from USD400 (USD500 monthly), with 5,000 to 250,000 Flowi Tokens/month and top-ups; 1 token per node run; third-party API and model usage is not included.
Common mistakes and when to avoid unification
Do not merge people because display names match, turn unknown states into false, or advertise a write because only one adapter supports it. Test each adapter against known, unknown, null and absent states. Choose direct source integrations when the task depends on source-specific fields or operations that the shared contract cannot express. A unified read view is useful only when its declared losses are acceptable to the consumer.
Related terms
API integration covers the individual request contract. Function calling concerns a model’s tool requests; those tools can consume a shared service contract.