All Docs
FeaturesNurtureHubUpdated March 25, 2026

Hardening External API Boundaries: Runtime Validation with Zod

Hardening External API Boundaries: Runtime Validation with Zod

Release: v1.0.94 · Supply Chain Security Control SCR-05


Background

NurtureHub connects to a wide range of external services to power its core features:

ProviderPurpose
OpenAIAI-generated nurture email sequences
ResendTransactional and marketing email delivery
TwilioSMS and voice alerts
agentOSNative CRM integration
Reapit / Alto / Street / LoopOpen API CRM sync
Meta / Google AdsLead source attribution
ApifyWeb data enrichment

Every one of these providers owns their own API schema. They can change it — intentionally or not — at any time.


The Problem with Type Assertions Alone

TypeScript is a compile-time tool. When you write:

const response = await fetch(url);
const data = await response.json() as OpenAICompletionResponse;

...TypeScript trusts you. It does not verify that what comes back from the network actually matches OpenAICompletionResponse. At runtime, the types are gone.

This means that if OpenAI were to rename a field, restructure the choices array, or introduce a new required property, the application would receive data it didn't expect — and silently treat missing values as undefined. Depending on where that undefined propagated, the result could range from a broken email sequence to an unhandled exception with an opaque stack trace.

The agentOS adapter used optional chaining (?.) throughout, which helped avoid hard crashes, but it masked the underlying issue rather than surfacing it.


The Fix: Zod Schemas at the API Boundary

Zod is a TypeScript-first schema declaration and validation library. It validates data at runtime — exactly where TypeScript cannot help — and throws descriptive errors when the shape of incoming data does not match expectations.

With v1.0.94, Zod schemas have been added for the most critical external response shapes:

OpenAI Completion Responses

The choices array is central to how NurtureHub generates email sequences. A schema now enforces that choices exists, is an array, and that each entry contains the expected message structure before any downstream processing occurs.

Resend Send Responses

The id field returned by Resend is used to track delivery status and correlate send events. The schema ensures this field is always present as a string — preventing silent tracking failures if Resend's response format changes.

CRM Contact Page Responses (Reapit)

Reepit's paged contact responses feed the bidirectional CRM sync. The schema validates the pagination envelope and contact record structure, so a provider-side schema change (such as the previously undocumented _embeddeddata migration risk) is caught immediately at the boundary.

Primary file changed: src/lib/crm/adapters/reapit.ts


What This Changes for Developers

Before

// No runtime check — TypeScript trusts the cast
const completion = data as OpenAICompletionResponse;
const content = completion.choices[0].message.content; // silently undefined if schema drifted

After

// Zod parses and validates at runtime
const completion = OpenAICompletionSchema.parse(data);
const content = completion.choices[0].message.content; // throws ZodError with full detail if invalid

If a provider's response fails validation, a ZodError is thrown with a precise description of which field failed and why. This surfaces in logs and error tracking immediately, with full context — rather than bubbling up as a confusing TypeError: Cannot read properties of undefined several frames deep.


Compatibility

This is a purely additive change. No existing integration logic, data flow, or API contract has been modified. The Zod schemas sit at the inbound data boundary only, and all valid responses that were handled correctly before will continue to pass through without any change in behaviour.


Why This Matters

External API schemas are outside NurtureHub's control. Providers deprecate fields, change response structures, and introduce new required keys — often without notice. Treating the boundary between NurtureHub and the outside world as a trusted, static interface is a supply chain risk.

Runtime validation at that boundary means:

  • Schema drift is detected immediately, not discovered when a customer reports a broken email sequence.
  • Error messages are precise, pointing directly at the provider and field that changed.
  • The rest of the application can be trusted to work with well-shaped data, reducing the need for defensive ?. chaining throughout the codebase.

This change implements supply chain security control SCR-05 as part of NurtureHub's ongoing API reliability programme.