Architecture Decisions
Key architectural decisions in the Amigo API design and their rationale.
Key architectural decisions in the Amigo AI API design and their rationale.
ADR-001: NDJSON over SSE for Streaming
Status: Accepted
Context: Conversation creation and interaction endpoints stream multiple events back to the client. We needed to choose between Server-Sent Events (SSE), WebSocket, and Newline-Delimited JSON (NDJSON) for the HTTP streaming protocol.
Decision: Use NDJSON (newline-delimited JSON) for all streaming HTTP endpoints.
Rationale:
Parsing complexity
Parse each line as JSON
Event ID management, retry logic, type field parsing
Full duplex protocol, framing
Client support
Works with any HTTP client
Requires EventSource API or polyfill
Requires WebSocket API
Content types
Standard application/x-ndjson
text/event-stream
Binary frames
Proxy compatibility
Passes through standard HTTP proxies
Some proxies buffer SSE streams
Requires upgrade
Error handling
Standard HTTP status codes
Limited to stream reconnection
Close codes
Binary data
Base64 in JSON fields
Text only
Native binary support
Key advantages for our use case:
Simplicity: Each line is a self-contained JSON object. No event ID tracking or reconnection logic needed.
Language agnostic: Works with any language that can read lines from an HTTP response body.
Discriminated unions: Each event has a
typefield that maps directly to a typed discriminated union in both SDKs.Progressive parsing: Clients can process events as they arrive without buffering the entire response.
Consequences:
Clients must handle newline-delimited parsing (both SDKs provide this via async generators)
No built-in reconnection semantics (clients implement retry via the SDK retry middleware)
WebSocket is used separately for the real-time voice endpoint where bidirectional streaming is required
ADR-002: JWT over Session Tokens
Status: Accepted
Context: The API needs an authentication mechanism that works across multiple services, supports token refresh, and can be verified without database lookups.
Decision: Use JWT (JSON Web Token) bearer tokens obtained via API key exchange.
Authentication flow:
Client exchanges API key + API key ID for a JWT via
POST /user/signin_with_api_keyJWT is sent as
Authorization: Bearer {token}on subsequent requestsSDKs proactively refresh tokens 5 minutes before expiry
Rationale:
Stateless verification
Yes (signature check)
No (DB lookup)
No (DB lookup)
Cross-service
Yes (shared signing key)
No (service-specific)
Yes
Expiration
Built-in exp claim
Manual tracking
Never expires
Revocation
Requires token blacklist
Delete from DB
Requires rotation
Payload
Standard claims (sub, org, exp)
Opaque identifier
Opaque identifier
Token size
Larger (~1KB)
Small (~32 bytes)
Small (~64 bytes)
Key advantages:
Stateless: API servers verify tokens without hitting a session store, enabling horizontal scaling.
Standard claims:
sub(user ID),org(organization),exp(expiration) are standard JWT fields that any JWT library can validate.Proactive refresh: The 5-minute refresh window in both SDKs ensures uninterrupted operation without 401 retry overhead.
Separation of concerns: Long-lived API keys never travel over the wire after initial exchange. Short-lived JWTs limit exposure if intercepted.
Consequences:
Tokens are larger than session IDs, increasing request overhead slightly
SDKs must implement token refresh logic (both do, with race condition protection via locks/shared promises)
Token revocation requires additional infrastructure (not currently needed for API key-based auth)
ADR-003: service_hierarchical_state_machine Naming
Status: Accepted
Context: The platform documentation refers to "Context Graphs" - the state-based architecture that structures agent reasoning and conversation flow. The API needs a field name for this entity.
Decision: Use service_hierarchical_state_machine as the API resource name.
Rationale:
The name was chosen to be technically precise about what the entity represents:
service: Scoped to a service (the deployment unit that combines agent + state machine + tools)hierarchical: States can be nested hierarchically (parent states containing child states)state_machine: The fundamental computational model - a finite set of states with defined transitions
Why not context_graph?
Disambiguation
"Graph" is overloaded - could be confused with knowledge graphs, graph databases, or dependency graphs. "State machine" is unambiguous in computer science.
Implementation fidelity
The implementation IS a hierarchical state machine. Exit conditions define transitions. States have types (Action, Decision, Reflection, ToolCall).
API stability
Technical names are less likely to change than marketing names. The platform docs can rename "Context Graph" without breaking the API.
Self-documenting
A developer seeing service_hierarchical_state_machine in a payload immediately understands the data structure without needing to read documentation.
Consequences:
Verbose field name increases payload sizes slightly
Developers must learn the mapping between platform terminology ("Context Graph") and API names
The terminology mapping page bridges this gap (see Terminology Mapping)
See also: Terminology Mapping for the complete mapping between platform concepts and API names.
Last updated
Was this helpful?

