Base URL: https://willowriverautomation.com/relay/v1
Set this in your shell for the examples below:
export BASE="https://willowriverautomation.com/relay/v1"
All responses are JSON. Errors:
{ "error": { "code": "string", "message": "string", "details": {} } }
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/auth/signup |
Public | Create account (sends verification email) |
POST |
/auth/verify |
Public | Verify email with one-time token from email |
POST |
/auth/resend-code |
Public | Resend verification token |
POST |
/auth/forgot-password |
Public | Request password reset (uniform response) |
POST |
/auth/reset-password |
Public | Reset password with token from email |
POST |
/auth/api-key |
Public (email+password) | Mint API key (shown once) |
GET |
/account |
API key | Org profile, plan, usage |
DELETE |
/account |
API key + email/password | Delete account and org data |
GET |
/account/audit-log |
API key | Org audit trail (paginated) |
PATCH |
/account/billing |
API key | Toggle allow_overage |
POST |
/billing/checkout |
API key | Stripe Checkout URL (starter or growth) |
POST |
/billing/portal |
API key | Stripe Customer Portal URL |
GET |
/billing/usage |
API key | Usage snapshot + overage rate |
Limits match the Intake Relay pricing page. Full breakdown: billing & quotas.
| Limit | Free | Starter | Growth | Scale |
|---|---|---|---|---|
| Intake submissions / period | 10 | 2,000 | 20,000 | Custom |
| Flows | 1 | 5 | Unlimited | Unlimited |
| Webhook endpoints (active) | 1 | 3 | 10 | Custom |
Version pinning (?version=N) |
Yes | Yes | Yes | Yes |
| Webhook retries + delivery log | Yes | Yes | Yes | Yes |
| File uploads | No | Yes | Yes | Yes |
| File upload size (per file) | No | 10 MB | 10 MB | By request |
| Uploaded file retention | No | 60 days | 60 days | 60 days |
GET /account){
"plan": "starter",
"plan_status": "active",
"allow_overage": false,
"usage_period_start": "2026-05-01T00:00:00+00:00",
"usage_period_end": "2026-06-01T00:00:00+00:00",
"submissions": { "used": 42, "limit": 2000, "remaining": 1958 },
"flows": { "used": 2, "limit": 5 },
"webhooks": { "used": 1, "limit": 3 }
}
limit: null means unlimited (Growth/Scale flows, Scale submissions/webhooks). Starter and Growth can opt in to submission overage via PATCH /account/billing.
Intake submit and file presign require ?version=N pointing at a published flow version unless the flow explicitly allows latest intake. Missing version returns version_required (400). See Intake flows.
All plans include automatic retries (5 attempts, exponential backoff) and GET /flows/{flow_id}/webhooks/deliveries. See Webhooks.
Management API — Authorization: Bearer <api_key>
Public intake — X-Embed-Key: <embed_key> (per-flow, safe for frontend)
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/flows |
API key | Create intake flow |
GET |
/flows |
API key | List flows |
GET |
/flows/{flow_id} |
API key | Get flow metadata |
POST |
/flows/{flow_id}/rotate-embed-key |
API key | Rotate embed key |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/flows/{flow_id}/versions |
API key | Create draft version |
PUT |
/flows/{flow_id}/versions/{v} |
API key | Update draft |
POST |
/flows/{flow_id}/versions/{v}/publish |
API key | Publish (immutable) |
GET |
/flows/{flow_id}/versions/{v} |
API key or embed key | Fetch schema |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/flows/{flow_id}/intake?version=N |
API key or embed key | Submit intake |
GET |
/intake/{id} |
API key | Get submission |
GET |
/flows/{flow_id}/intake |
API key | List submissions |
GET |
/intake/{id}/events |
API key | Event log for submission |
curl -X POST "$BASE/flows/$FLOW_ID/intake?version=1" \
-H "X-Embed-Key: $EMBED_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: user-abc-signup" \
-d '{"data":{"email":"dev@acme.com","plan":"pro"}}'
Idempotency: Pass Idempotency-Key header. Duplicate keys return the original submission with idempotent_replay: true.
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/flows/{flow_id}/uploads/presign |
API key or embed key | Get presigned upload URL |
Plan requirement: File uploads require Starter, Growth, or Scale. Free accounts receive uploads_not_available (403) if presign or intake submission includes file attachments.
Pass ?version=N matching your pinned published flow version. The field_id must refer to a file type field on that version.
On paid plans, presigned uploads are capped at 10 MB per file (UPLOAD_MAX_BYTES). The blob must exist in GCS before intake submission accepts the file reference. Files are stored in Google Cloud Storage under your organization prefix. Uploaded objects are automatically deleted after 60 days via bucket lifecycle policy — Intake Relay is not long-term file archival. Download or copy files to your own systems via webhooks if you need longer retention.
Scale plan: Larger per-file limits (for example video) are available by special request only, subject to custom pricing and manual configuration. Contact Willow River Automation before building flows that depend on higher limits.
curl -X POST "$BASE/flows/$FLOW_ID/uploads/presign?version=1" \
-H "X-Embed-Key: $EMBED_KEY" \
-H "Content-Type: application/json" \
-d '{"field_id":"resume","filename":"cv.pdf","content_type":"application/pdf"}'
Success (200):
{
"storage_key": "uploads/{org_id}/{flow_id}/{uuid}/cv.pdf",
"upload_url": "https://storage.googleapis.com/...",
"expires_at": "2026-05-21T12:00:00Z"
}
Free plan (403):
{
"error": {
"code": "uploads_not_available",
"message": "File uploads are not available on the free plan. Upgrade to Starter or Growth.",
"details": { "plan": "free", "...": "..." }
}
}
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/flows/{flow_id}/webhooks |
API key | Register endpoint |
GET |
/flows/{flow_id}/webhooks |
API key | List endpoints |
DELETE |
/flows/{flow_id}/webhooks/{id} |
API key | Deactivate endpoint |
GET |
/flows/{flow_id}/webhooks/deliveries |
API key | Delivery log |
| Method | Path | Description |
|---|---|---|
GET |
/health |
Health check |
GET |
/events |
Registered workflow event types |
| Code | HTTP | Meaning |
|---|---|---|
unauthorized |
401 | Missing or invalid credentials |
not_found |
404 | Resource not found |
validation_error |
400 | Missing required request field |
validation_failed |
400 | Intake data failed schema validation |
version_required |
400 | Must pass ?version=N |
immutable_version |
409 | Cannot edit published version |
duplicate_slug |
409 | Flow slug already exists in org |
uploads_not_available |
403 | File uploads require Starter, Growth, or Scale (not on Free) |
quota_exceeded |
429 | Plan limit reached: submissions, flows, or webhook endpoints |
billing_inactive |
402 | Subscription past due or canceled |