400 or 404 today.
This page is the grep list. Each row is the old string to search your codebase
for and the string to replace it with. The generated
Public API v1 reference is
the authority on the exact request and response shapes.
1. Read One Row by Its Path
The old trick was to call a list with an id filter and takedata[0]. Those
query keys are gone and now return 400 INVALID_REQUEST with param naming
the key. Every detail read returns the bare row, so drop the data[0]
unwrap too.
New reads with no predecessor:
GET /v1/automations/audience-runs/{audienceRunId},
GET /v1/automations/trigger-instances/{triggerInstanceId}, and
GET /v1/emails/{emailId}/inbox-placement-tests/{testId}.
The ?include= opt-ins still work, now on the path read:
GET /v1/emails/{emailId}?include=html,versions. The full list is in
Pagination.
An id in a query string is not always wrong.
GET /v1/sends and
GET /v1/automations/runs still take automationId, automationRunId,
audienceRunId, and triggerInstanceId as deliberate join filters,
because those return many rows, not one.2. Sends Moved to Their Own Root
/v1/analytics keeps exactly three reports: overview, automations, and
the unified events feed. See
Monitor campaign sends.
3. Lifecycle Changes Are Action Sub-Paths
A status is no longer something you PATCH onto a row.
Cancel answers
200 with the new status and, for an automation run, the
previousStatus it left. The readiness body is bare:
{ ready, blockers[], payloadSchema, endpoint, publishedAutomations, counts },
with no details wrapper.
4. Renamed Request and Response Fields
Those two were the last snake_case fields on the wire. Everything v1 accepts
and returns is camelCase now.
Send rows gained
automationId, nodeId, automationRunId, audienceRunId,
and triggerInstanceId, so an automation delivery can be joined back to the
run that produced it without a second call.
Two responses became explicit rather than a bare count:
POST /v1/contacts/batch-delete returns { deletedCount, notFound[] }, and
DELETE /v1/api-keys/{keyId} returns { keyId, deleted, revokedAt? }.
The event row field is still
recipientEmail. Only the query filter was
renamed to recipient.TriggerInstance.state Became an Enum
A fired trigger’s state used to be a free string. It is now a closed set you
can branch on:
fired means every matched automation started. partially_fired means some
starts are still being retried, so it is not a terminal answer: read the
instance again rather than treating it as a failure.
5. One Status Vocabulary
Runs, sends, audience builds, and inbox-placement tests all report the same set. Anything typed against an old spelling in a status filter is now400 INVALID_REQUEST.
A step or node inside a run reports
running | completed | failed | skipped.
An email design reports generating | ready | failed, so the old
streaming and complete spellings are gone there too. A synchronous test
send now resolves to 200 { status: 'completed', recipient }.
6. Errors
New this cleanup:
403 ORG_SCOPE_REQUIRED when a brand-bound key calls an
operation that acts on the organization, such as GET /v1/usage. A person
missing the role still gets 403 INSUFFICIENT_ROLE. See
Organization and brand credentials.
The full catalog, generated from the specification, is on
Errors.
7. TypeScript SDK 10
Twenty methods were added, mostly the detail reads and the send root:
And these were removed or retargeted:
analytics keeps only the reports: overview, events, automations.
8. CLI 0.7
brew-cli 0.7 depends on @brew.new/sdk 10. Six commands were retargeted at
the new routes, and the command names you already type are unchanged, so
brew-cli automations triggers ready still works. New commands cover the
detail reads, the sends root, trigger instances, and the four lifecycle
actions. Raw brew-cli api calls in your scripts are not retargeted for
you: they go straight to the path you typed, so grep those against section 1.
Migration Checklist
1
Grep for the retired query keys
Search for
?emailId=, ?audienceId=, ?automationId=, ?automationRunId=,
?triggerEventId=, ?sendId=, ?triggerInstanceId=, and ?domainId=.
Each one that reads a single row becomes a path read.2
Grep for the analytics paths
analytics/sends, analytics/campaigns, and analytics/trigger-instances
have no replacement at the same path.3
Grep for old status strings
'sent', 'sending', 'partially_sent', 'pending', 'streaming',
'complete', and 'cancelled' in filters and comparisons.4
Drop the data[0] unwrap
A detail read returns the row.
body.data[0] will be undefined.5
Drop runId from send handling
POST /v1/sends no longer returns one. Persist sendId instead.6
Grep for snake_case request keys
dry_run and stop_in_flight are rejected. Send dryRun and
stopInFlight.7
Re-check your credential
If any call now returns
403 ORG_SCOPE_REQUIRED, mint an
organization-scoped key for it. See
Organization and brand credentials.See Also
- Pagination: the detail-read table and the cursor loop.
- Errors: the generated code catalog.
- Async jobs & polling: the poll loops, rewritten for the path reads.
- Changelog: the dated record of API changes.
Need Help?
Our team is ready to support you at every step of your journey with Brew. Choose the option that works best for you:- Self-Service Tools
- Talk to Our Team
Search Documentation
Type in the “Ask any question” search bar at the top left to instantly find relevant documentation pages.
ChatGPT/Claude Integration
Click “Open in ChatGPT” at the top right of any page to explore it further with ChatGPT or Claude.