curl --request GET \
--url https://brew.new/api/v1/flows \
--header 'Authorization: Bearer <token>'import requests
url = "https://brew.new/api/v1/flows"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://brew.new/api/v1/flows', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://brew.new/api/v1/flows",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://brew.new/api/v1/flows"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://brew.new/api/v1/flows")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://brew.new/api/v1/flows")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"slug": "notion.com",
"brand": {
"domain": "notion.com",
"name": "Notion",
"logo": "https://cdn.brew.new/brand/fetched-logo/notion.com/logo.png"
},
"title": "Notion onboarding flow",
"type": "signup",
"category": "welcome",
"categoryLabel": "Welcome",
"emailCount": 6,
"spanDays": 14,
"remixCount": 12,
"previewImages": [
"https://cdn.brew.new/email-preview-notion-1.png",
"https://cdn.brew.new/email-preview-notion-2.png",
"https://cdn.brew.new/email-preview-notion-3.png"
],
"publishedAt": "2026-09-01T12:00:00.000Z",
"updatedAt": "2026-09-01T12:00:00.000Z",
"anchor": "signedUpAt",
"steps": [
{
"order": 1,
"dayOffset": 0,
"delayDays": 0,
"subject": "Welcome to Notion",
"previewText": "Here’s how to set up your first page.",
"category": "welcome",
"categoryLabel": "Welcome",
"emailId": "pt1_k97nvhqe6xgyj67g58ajwj1tj58egexx",
"previewImage": "https://cdn.brew.new/email-preview-notion-1.png"
},
{
"order": 2,
"dayOffset": 2.1,
"delayDays": 2.1,
"subject": "Three templates to try today",
"category": "education",
"categoryLabel": "Education",
"emailId": "pt1_k97s409kdrzbeajqffq6011wfn8egmdq",
"previewImage": "https://cdn.brew.new/email-preview-notion-2.png"
}
]
}
],
"pagination": {
"limit": 100,
"cursor": null,
"hasMore": false
}
}{
"error": {
"code": "INVALID_REQUEST",
"type": "invalid_request",
"message": "Request validation failed.",
"suggestion": "Fix the field reported in `param` and retry.",
"docs": "https://docs.brew.new/api-reference/api/errors",
"param": "sort"
}
}{
"error": {
"code": "INVALID_API_KEY",
"type": "authentication_error",
"message": "The provided API key is invalid.",
"suggestion": "Check the API key format and retry with a valid active key.",
"docs": "https://docs.brew.new/api-reference/api/authentication"
}
}{
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"type": "authorization_error",
"message": "The caller does not have the required permission.",
"suggestion": "Use an API key or session with the required permission.",
"docs": "https://docs.brew.new/api-reference/api/authentication",
"param": "emails"
}
}{
"error": {
"code": "FLOW_NOT_FOUND",
"type": "not_found",
"message": "No public flow matches slug 'no-such-brand.example'.",
"suggestion": "List flows with GET /v1/flows and use a returned `slug` (the brand domain, e.g. `notion.com`).",
"docs": "https://docs.brew.new/api-reference/api/errors",
"param": "slug"
}
}{
"error": {
"code": "RATE_LIMITED",
"type": "rate_limit",
"message": "Too many requests.",
"suggestion": "Wait for the retry window before sending another request.",
"docs": "https://docs.brew.new/api-reference/api/rate-limits",
"retryAfter": 42
}
}{
"error": {
"code": "INTERNAL_ERROR",
"type": "internal_error",
"message": "An unexpected error occurred.",
"suggestion": "Retry the request. If it keeps failing, contact support.",
"docs": "https://docs.brew.new/api-reference/api/errors"
}
}List flows
Public email flows — one brand’s real onboarding or newsletter sequence, with the day each email landed — under { data, pagination }. Omit slug to LIST cards (filter ?brand=, ?category=, ?type=; order with ?sort=newest|emails|span|remixes; or ?semantic= for relevance-ranked search). Pass ?slug=<brand domain> to fetch ONE flow → data: [flow] with every step’s subject, previewText, dayOffset, delayDays, category, previewImage and emailId; add ?include=html for each step’s rendered HTML (best-effort per step: a step whose body is no longer servable comes back without html). anchor and steps are detail-only — a LIST card never carries them. A step’s emailId is a template reference: use it as referenceEmailId on POST /v1/emails, or look it up on GET /v1/templates. Organization-wide. The list is the gallery’s own set — the same bounded corpus the site shows (a few hundred flows today) — paged with limit/cursor.
curl --request GET \
--url https://brew.new/api/v1/flows \
--header 'Authorization: Bearer <token>'import requests
url = "https://brew.new/api/v1/flows"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://brew.new/api/v1/flows', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://brew.new/api/v1/flows",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://brew.new/api/v1/flows"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://brew.new/api/v1/flows")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://brew.new/api/v1/flows")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"slug": "notion.com",
"brand": {
"domain": "notion.com",
"name": "Notion",
"logo": "https://cdn.brew.new/brand/fetched-logo/notion.com/logo.png"
},
"title": "Notion onboarding flow",
"type": "signup",
"category": "welcome",
"categoryLabel": "Welcome",
"emailCount": 6,
"spanDays": 14,
"remixCount": 12,
"previewImages": [
"https://cdn.brew.new/email-preview-notion-1.png",
"https://cdn.brew.new/email-preview-notion-2.png",
"https://cdn.brew.new/email-preview-notion-3.png"
],
"publishedAt": "2026-09-01T12:00:00.000Z",
"updatedAt": "2026-09-01T12:00:00.000Z",
"anchor": "signedUpAt",
"steps": [
{
"order": 1,
"dayOffset": 0,
"delayDays": 0,
"subject": "Welcome to Notion",
"previewText": "Here’s how to set up your first page.",
"category": "welcome",
"categoryLabel": "Welcome",
"emailId": "pt1_k97nvhqe6xgyj67g58ajwj1tj58egexx",
"previewImage": "https://cdn.brew.new/email-preview-notion-1.png"
},
{
"order": 2,
"dayOffset": 2.1,
"delayDays": 2.1,
"subject": "Three templates to try today",
"category": "education",
"categoryLabel": "Education",
"emailId": "pt1_k97s409kdrzbeajqffq6011wfn8egmdq",
"previewImage": "https://cdn.brew.new/email-preview-notion-2.png"
}
]
}
],
"pagination": {
"limit": 100,
"cursor": null,
"hasMore": false
}
}{
"error": {
"code": "INVALID_REQUEST",
"type": "invalid_request",
"message": "Request validation failed.",
"suggestion": "Fix the field reported in `param` and retry.",
"docs": "https://docs.brew.new/api-reference/api/errors",
"param": "sort"
}
}{
"error": {
"code": "INVALID_API_KEY",
"type": "authentication_error",
"message": "The provided API key is invalid.",
"suggestion": "Check the API key format and retry with a valid active key.",
"docs": "https://docs.brew.new/api-reference/api/authentication"
}
}{
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"type": "authorization_error",
"message": "The caller does not have the required permission.",
"suggestion": "Use an API key or session with the required permission.",
"docs": "https://docs.brew.new/api-reference/api/authentication",
"param": "emails"
}
}{
"error": {
"code": "FLOW_NOT_FOUND",
"type": "not_found",
"message": "No public flow matches slug 'no-such-brand.example'.",
"suggestion": "List flows with GET /v1/flows and use a returned `slug` (the brand domain, e.g. `notion.com`).",
"docs": "https://docs.brew.new/api-reference/api/errors",
"param": "slug"
}
}{
"error": {
"code": "RATE_LIMITED",
"type": "rate_limit",
"message": "Too many requests.",
"suggestion": "Wait for the retry window before sending another request.",
"docs": "https://docs.brew.new/api-reference/api/rate-limits",
"retryAfter": 42
}
}{
"error": {
"code": "INTERNAL_ERROR",
"type": "internal_error",
"message": "An unexpected error occurred.",
"suggestion": "Retry the request. If it keeps failing, contact support.",
"docs": "https://docs.brew.new/api-reference/api/errors"
}
}Authorizations
Send your Brew API key as Authorization: Bearer brew_xxx.
Query Parameters
Fetch ONE flow by its brand domain (e.g. notion.com) → data: [flow] with every steps[] entry. Omit to LIST.
1 - 253^[a-z0-9.-]+$Detail-only expansions, comma-separated. html adds each step’s rendered HTML — up to 12 emails, so ask for it only when you will read them. Best-effort per step: a step whose body is no longer servable comes back without html instead of failing the flow.
Exact brand domain filter for LIST, e.g. vercel.com.
1Filter LIST by the flow’s dominant step category (welcome, newsletter, promotion, education, …).
verification, transactional, direct_sales, welcome, promotion, newsletter, education, abandoned_cart, winback, other Filter LIST by how the sequence starts: signup (after creating an account) or newsletter (after subscribing).
newsletter, signup Semantic search over what the sequences are about — “developer onboarding drip”, “trial expiry win-back”. Results come back in relevance order and sort is ignored.
1 - 500LIST order: newest (default), emails (longest sequences first), span (sequences that run the longest first), remixes (most remixed first).
newest, emails, span, remixes 1 - 512Was this page helpful?