FIN API Layer Spec

Date: 2026-05-15 Status: Draft v0.1 Related: docs/architecture/FIN_MCP_SURFACE_ARCHITECTURE_2026-05-15.md, docs/engineering/FIN_INTRANET_HUMAN_AGENT_PROJECTION_ENGINEERING_SPEC_2026-05-13.md, docs/engineering/FIN_AGENTIC_HARNESS_SPEC_2026-05-15.md

0. Purpose

MCP serves agents. The API layer serves systems and UIs — customer portal calls, vendor portal calls, webhook ingest from external systems, embedded customer/vendor-facing endpoints, FSMA / GS1 / USDA integration endpoints.

Many endpoints have MCP equivalents. The implementations share code; the contracts and auth differ. This doc specifies the HTTP/webhook surface that is NOT MCP, and the open-standards adopters.

If API contracts diverge across docs, this doc is the source of truth.

1. Scope

  • HTTP REST endpoints for UI and system callers
  • Webhook receivers (Telnyx, email providers, GitHub, payment processors, ERP integrations)
  • WebSocket / SSE for real-time UI streams
  • Open-standards integration (GS1 GDSN, FSMA 204, USDA AMS Market News, eBL)
  • API versioning, auth, rate limiting, idempotency, pagination, observability

Out of scope:

  • MCP tool catalog (see MCP Surface Architecture)
  • Internal service-to-service RPC (not externally exposed)

2. URL Layout

All API endpoints under /api/v1/ with prefix routing per audience:

PathAudienceAuthentication
/api/v1/internal/*FIN ops, CImTLS + service-account JWT
/api/v1/tenant/<tenant_id>/*tenant Workspace UI, tenant integrationstenant OAuth
/api/v1/portal/customer/*customer portal UIcustomer OAuth
/api/v1/portal/vendor/*vendor portal UIvendor OAuth
/api/v1/public/*public consumer site, anonymous fetchesoptional API key
/api/v1/federal/*federal-mode dashboardsFedRAMP IdP
/api/v1/webhooks/<provider>external systems (Telnyx, GitHub, etc.)signature verification

URL versioning, not header versioning. Major-version bumps create parallel routes (/api/v2/...); minor / patch bumps stay within v1.

3. REST Conventions

3.1 Resource Paths

  • Collections: /api/v1/tenant/<tenant_id>/lots/
  • Items: /api/v1/tenant/<tenant_id>/lots/<lot_id>
  • Sub-collections: /api/v1/tenant/<tenant_id>/lots/<lot_id>/qc_events/
  • Action endpoints (when REST verbs don’t fit): POST /api/v1/tenant/<tenant_id>/lots/<lot_id>:hold

3.2 Methods

  • GET (list / fetch)
  • POST (create or trigger action; never PUT for create)
  • PATCH (partial update; never PUT for partial)
  • PUT (full replacement, rarely used)
  • DELETE (only soft-delete, never destructive)

3.3 Query Parameters

Standard parameters across collection endpoints:

  • limit (default 50, max 200)
  • cursor (opaque pagination cursor; never offset-based)
  • filter[<field>]=<value> (typed filters)
  • sort=<field> or sort=-<field> (negative prefix for descending)
  • fields=<comma_separated> (sparse fieldset)
  • include=<relation> (related resources to expand)

3.4 Response Envelope

{
  "data": [...],
  "meta": {
    "next_cursor": "...",
    "total_estimate": 12345
  },
  "links": {
    "self": "...",
    "next": "..."
  }
}

Item responses use the same envelope with data as an object instead of an array.

3.5 Error Envelope

{
  "error": {
    "type": "validation_error",
    "code": "lot_id_invalid",
    "message": "Lot ID format is invalid",
    "details": {...},
    "request_id": "req_01H...",
    "doc_url": "https://docs.fin.app/errors/lot_id_invalid"
  }
}

Error type taxonomy: validation_error, auth_error, permission_error, not_found, conflict, rate_limit_exceeded, dependency_failure, internal_error.

4. Authentication

4.1 Tenant OAuth

Standard OAuth 2.1 with PKCE for tenant users:

  • Authorization endpoint: /oauth/authorize
  • Token endpoint: /oauth/token
  • Refresh: rotation enabled, 30-day expiry
  • Access token: 1-hour expiry
  • Scopes: per-resource, per-action (e.g., lots:read, lots:write, customers:read)
  • JWT claims: tenant_id, actor_id, actor_role, tier, scopes[], exp, iat, jti

4.2 Customer / Vendor Portal OAuth

Portal users are external actors with limited scope:

  • Login via email magic link OR SSO with tenant-configured providers
  • Customer JWT scopes: customer_self:read, customer_self:orders, customer_self:invoices, harvest_directory:read
  • Vendor JWT scopes: vendor_self:read, vendor_self:lots, vendor_self:qc_outcomes, harvest_directory:read
  • Cross-customer / cross-vendor access strictly refused at route

4.3 Public Anonymous

Public endpoints accept anonymous calls with stricter rate limits. Authenticated public calls (with Tier 0 API key) get higher limits and quota-tracked usage.

4.4 Webhook Auth

Each webhook provider has signature verification:

  • Telnyx: HMAC-SHA256 with shared secret
  • GitHub: HMAC-SHA256 with webhook secret
  • Email providers (Postmark / SendGrid / Mailgun): signature per provider
  • Payment processors: provider-specific verification
  • Custom partner webhooks: HMAC-SHA256 with per-partner secret

Failed signature verification returns 401, emits webhook.signature_invalid event, never processes payload.

5. Idempotency

5.1 Idempotency Keys

Mutating endpoints accept Idempotency-Key: <client_supplied_uuid> header. The server caches the response for 24 hours; same key returns the same response.

Required on:

  • POST /api/v1/tenant/<>/lots/
  • POST /api/v1/tenant/<>/orders/
  • POST /api/v1/tenant/<>/payments/
  • Any endpoint triggering a billable LLM call
  • Any endpoint that emits an event with external side effects

5.2 Conflict Detection

If a different request body comes in with the same idempotency key, return 409 IdempotencyKeyConflict and emit api.idempotency_conflict_detected.

6. Pagination

6.1 Cursor-Based Only

Every list endpoint uses cursor pagination. Offset pagination is explicitly disallowed (event stream + projection state makes offsets non-stable).

Cursors are opaque, base64-encoded JSON carrying:

{
  "anchor": "knowledge_key_or_event_id",
  "sort_field": "created_at",
  "sort_dir": "desc",
  "version": "v1"
}

6.2 Stable Sort

All paginated endpoints have stable sort (typically (sort_field DESC, id DESC) as tiebreaker). Without stable sort, cursor pagination skips or repeats records.

6.3 Default Page Size

50 records, max 200. Larger requests return 400 with the cap.

7. Webhooks

7.1 Inbound Webhook Providers

The harness receives inbound webhooks from:

  • Telnyx: SMS receive, voice call events, message status updates
  • Email providers: new email at arrivals@, intel@, others
  • GitHub: vault commits (per Projection Spec §5.1)
  • Stripe (or chosen processor): payment events, dispute events, subscription changes
  • Customer / vendor system integrations: when tenants set up outbound webhooks to FIN
  • Per-tenant custom: ERP system integrations (Famous, Produce Pro via scraping middleware, Silver Mountain via export drops)

7.2 Outbound Webhook (FIN → Customers)

Tenants and Tier 0 customers can subscribe to webhook deliveries from FIN:

  • lot.received → tenant integration
  • recall.initiated → customer / vendor systems
  • commodity_situation.published → Tier 0 subscribers

Delivery:

  • HMAC-SHA256 signed
  • At-least-once delivery with exponential backoff (10 retries over 24h)
  • Subscriber acknowledges with 2xx; failures retry
  • Subscriber webhook health surfaces in tenant dashboards
  • Dead-letter after 24h, emit webhook.outbound_dead_lettered

7.3 Webhook Schema Versioning

Outbound webhook payloads carry schema version. Subscribers pin a version. Major-version bumps fork the delivery (subscribers explicitly migrate).

8. Real-Time (WebSocket / SSE)

8.1 Live UI Streams

The Workspace UI uses WebSocket or SSE for:

  • Lot status updates
  • Agent message stream during active session
  • Document index refresh notifications
  • Channel health alerts
  • Inbound message arrival (email, SMS, voice)

8.2 Connection Model

  • WebSocket for bidirectional (agent chat)
  • SSE for one-way push (status updates)
  • Connection authenticated with same OAuth JWT
  • Subscription scoped per actor, refreshed on token rotation

8.3 Backpressure

Server can throttle, drop oldest, or close per per-stream policy. Clients reconnect with last event ID to catch up.

9. Open Standards Integration

Adopt established standards where they exist. Don’t reinvent.

9.1 GS1 GDSN (Product Master Data)

  • Inbound: tenants subscribed to GDSN feeds get product master data ingested as knowledge.domain = product
  • Outbound: tenant product catalog can publish to GDSN if tenant is a brand owner
  • Mappings: GS1 attribute set → entity_profile schema

9.2 FSMA Section 204 Traceability

  • Per-lot FSMA Key Data Elements (KDEs) projected from events
  • Endpoint: GET /api/v1/tenant/<tenant_id>/fsma/lots/<lot_id> returns the FSMA-compliant record
  • Endpoint: POST /api/v1/tenant/<tenant_id>/fsma/filings for agency-initiated requests
  • Exports in FDA-specified format (CSV, JSON, or XML per the agency’s current spec)
  • Real-time generation; no precomputation of records FDA might never request

9.3 USDA AMS Market News

  • Inbound: USDA Market News commodity reports ingested as knowledge.domain = external_market_report rows for use in aggregate_intelligence projections
  • Outbound: Wave 5+ — FIN’s anonymized aggregates may feed back to AMS if a partnership develops

9.4 Electronic Bill of Lading (eBL)

  • For freight events, eBL records ingested at receiving
  • Conversion to lot.received events with provenance to the eBL
  • Outbound generation for shipments where tenant is shipper

9.5 GLN / GTIN Resolution

  • GET /api/v1/tenant/<tenant_id>/resolve/gln/<gln>vendor_profile or customer_profile doc
  • GET /api/v1/tenant/<tenant_id>/resolve/gtin/<gtin>product_profile or commodity_profile doc
  • Public Tier 0 may resolve GTIN to public commodity profile (anonymized)

9.6 USDA Organic / GAP Certification Verification

  • External verification via USDA Organic Integrity Database
  • Cached results with external_source_drift_monitor watching for changes

10. Customer Portal Endpoints (Tier 2+)

The customer portal is a separate UI experience with its own scoped API:

  • GET /api/v1/portal/customer/orders/ — customer’s orders
  • GET /api/v1/portal/customer/orders/<order_id> — single order detail
  • GET /api/v1/portal/customer/invoices/ — invoices
  • GET /api/v1/portal/customer/shipments/<shipment_id>/track — tracking
  • POST /api/v1/portal/customer/orders/ — place new order (if tenant enables order entry through portal)
  • POST /api/v1/portal/customer/communications — send message to tenant
  • GET /api/v1/portal/customer/profile — customer’s own profile

All scoped to the authenticated customer. Cross-customer attempts return 404 (not 403, to avoid information leakage).

11. Vendor Portal Endpoints (Tier 2+)

Symmetric to customer portal:

  • GET /api/v1/portal/vendor/lots/ — lots received from this vendor
  • GET /api/v1/portal/vendor/qc_outcomes/ — QC results
  • GET /api/v1/portal/vendor/payments/ — payment status
  • GET /api/v1/portal/vendor/profile — vendor’s own profile
  • POST /api/v1/portal/vendor/communications — message to tenant

12. Public Endpoints

  • GET /api/v1/public/commodities/ — public commodity list
  • GET /api/v1/public/commodities/<commodity_id> — commodity profile
  • GET /api/v1/public/regions/<region_id> — region intelligence
  • GET /api/v1/public/situation/<commodity_id>/<year_week> — weekly situation brief
  • GET /api/v1/public/recalls/ — active recalls (subscribable via webhook)
  • GET /api/v1/public/recalls/<recall_id> — recall detail

Tier 0 API key optional but recommended (higher rate limits).

13. Federal Endpoints

When federal mode is enabled:

  • GET /api/v1/federal/recall_traces/<lot_id> — federal-audience recall trace
  • GET /api/v1/federal/fsma/lots/<lot_id> — FSMA-formatted record
  • POST /api/v1/federal/inquiries — agency-initiated inquiry, returns inquiry_id, processed asynchronously
  • GET /api/v1/federal/inquiries/<inquiry_id> — inquiry status + result

Federal endpoints on separate FedRAMP-authorized infrastructure. Distinct deployment from public/tenant.

14. Rate Limiting & Quota

14.1 Per-Route Limits

  • Public anonymous: 60 req/min/IP
  • Public with API key (Tier 0 free): 1000 req/min
  • Tenant authenticated: 6000 req/min/tenant + 600 req/min/actor
  • Internal: 60000 req/min/service-account
  • Federal: per-contract negotiated

14.2 Burst Allowance

2x sustained rate for up to 60 seconds, then sustained applies.

14.3 Cost-Based Limits

Endpoints that trigger expensive backend operations (LLM calls, heavy joins, large exports) have additional per-actor cost budget enforcement.

15. Observability

15.1 Logging

Every request emits structured log with:

  • request_id
  • route
  • actor_id (when authenticated)
  • tenant_id (when applicable)
  • latency_ms
  • response_status
  • cost_cents (when applicable)

15.2 Tracing

OpenTelemetry trace propagation. Distributed traces span webhook → ingest → projection → tenant push.

15.3 Metrics

Prometheus-style metrics exposed at /internal/metrics (internal route only). Standard four golden signals plus:

  • per-tenant QPS
  • per-route p50/p95/p99 latency
  • webhook delivery success rate
  • idempotency cache hit rate

16. Open Standards Roadmap

StandardUse caseWaveStatus
GS1 GDSNProduct master data sync4planned
FSMA 204 KDEFederal traceability3 (per-tenant), 4 (federal)planned
USDA AMS Market NewsMarket context4planned
GLN / GTINIdentifier resolution3planned
eBLFreight + shipping5research
ASN (Advance Ship Notice)Receiving prep3planned
EDI 856 / 810 / 820Legacy retailer integration5research
OAuth 2.1 + PKCEAuth1committed
OpenAPI 3.1Spec documentation2planned

17. Tests

17.1 Unit Tests

  • pagination cursor encode/decode
  • idempotency-key collision detection
  • webhook signature verification per provider
  • OAuth token validation
  • rate limiter buckets

17.2 Integration Tests

  • end-to-end tenant OAuth flow
  • customer portal order placement → tenant agent receives → fulfillment
  • vendor portal QC outcome retrieval
  • public commodity profile fetch by anonymous + by Tier 0 key
  • webhook delivery + retry + dead-letter
  • FSMA 204 record generation matches FDA-published schema
  • cursor pagination stable across new records insertion
  • idempotent retry returns cached response

17.3 Open Standards Tests

  • GDSN payload roundtrip
  • FSMA record schema validation against FDA spec
  • USDA AMS feed ingest

18. Proof Gates

Wave 1 API is not complete until:

  • /api/v1/tenant/* routes with OAuth ship for the Nathel pilot
  • webhook ingest from Telnyx + GitHub + at least one email provider works end-to-end
  • cursor pagination ships and is stable across insertions
  • idempotency keys honored on all create endpoints

Wave 2 API is not complete until:

  • customer portal + vendor portal endpoints ship for Nathel
  • WebSocket / SSE streams active for Workspace UI
  • per-tenant rate limiting active

Wave 3 API is not complete until:

  • public endpoints deploy at api.harvestdirectory.org
  • FSMA 204 record generation passes FDA-spec test
  • outbound webhook subscriptions self-service

Wave 4 API is not complete until:

  • federal-mode endpoints on FedRAMP infrastructure
  • GDSN ingest + outbound
  • USDA AMS ingest
  • OpenAPI 3.1 spec generated and published

19. Stop Conditions

  • OAuth flows can’t be made compliant with tenant-side IdP requirements → return to architecture
  • FSMA record generation can’t match the FDA-published schema → defer federal-tenant offerings
  • webhook delivery can’t sustain at-least-once guarantees → return to infra design
  • public endpoint perf can’t sustain expected QPS without per-tenant isolation impact → return to capacity planning

20. Out Of Scope For v0.1

  • GraphQL endpoint
  • gRPC public surface
  • bulk-import API (use scheduled jobs)
  • A2A endpoint (handled by MCP, not API)
  • legacy EDI dialect support