Getting started with the Featurebase API
Create an API key, make your first request, sync a customer, page through lists and handle errors in about five minutes.
This guide takes you from zero to a working integration: create an API key, make your first request, write data, page through results and handle errors. You need about five minutes and a terminal, or any language that can send HTTP requests.
1. Create an API key
Open Settings → API in your Featurebase dashboard.
Create a key and copy it. It starts with
sk_.Store it as an environment variable, for example
FEATUREBASE_API_KEY.
🔒 An API key has full access to your workspace. Keep it on your server, never in browser or mobile app code, and never commit it to git. If a key leaks, delete it in Settings → API and create a new one.
2. Make your first request
List your feedback boards. Every request sends two headers: your key as a Bearer token, and the API version you build against.
curl https://do.featurebase.app/v2/boards \
-H "Authorization: Bearer $FEATUREBASE_API_KEY" \
-H "Featurebase-Version: 2026-08-19.orbit"const res = await fetch("https://do.featurebase.app/v2/boards", {
headers: {
Authorization: `Bearer ${process.env.FEATUREBASE_API_KEY}`,
"Featurebase-Version": "2026-08-19.orbit",
},
});
if (!res.ok) throw new Error(`Featurebase API error: ${res.status}`);
const boards = await res.json();
console.log(boards.map((b) => b.name));import os
import requests
API_KEY = os.environ["FEATUREBASE_API_KEY"]
res = requests.get(
"https://do.featurebase.app/v2/boards",
headers={
"Authorization": f"Bearer {API_KEY}",
"Featurebase-Version": "2026-08-19.orbit",
},
)
res.raise_for_status()
print([board["name"] for board in res.json()])A 200 response with your boards means your key works.
3. Pin your API version
Send Featurebase-Version: 2026-08-19.orbit on every request. Without the header, your workspace's default version applies, and that default can change when someone updates it in the dashboard. A pinned version never changes under you: new fields can appear in responses, but removed or renamed fields only come in a new version that you choose to adopt.
4. Write data: sync a customer
Most integrations start by syncing customers, so feedback, votes and conversations are tied to the right person. POST /v2/contacts creates the contact or updates it if it already exists, so you can call it every time a user signs up or changes.
curl https://do.featurebase.app/v2/contacts \
-X POST \
-H "Authorization: Bearer $FEATUREBASE_API_KEY" \
-H "Featurebase-Version: 2026-08-19.orbit" \
-H "Content-Type: application/json" \
-d @contact.json
# contact.json
{
"email": "jane@acme.com",
"name": "Jane Cooper",
"userId": "user_123"
}const res = await fetch("https://do.featurebase.app/v2/contacts", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.FEATUREBASE_API_KEY}`,
"Featurebase-Version": "2026-08-19.orbit",
"Content-Type": "application/json",
},
body: JSON.stringify({
email: "jane@acme.com",
name: "Jane Cooper",
userId: "user_123", // your own user ID
}),
});
const contact = await res.json();res = requests.post(
"https://do.featurebase.app/v2/contacts",
headers={
"Authorization": f"Bearer {API_KEY}",
"Featurebase-Version": "2026-08-19.orbit",
},
json={
"email": "jane@acme.com",
"name": "Jane Cooper",
"userId": "user_123", # your own user ID
},
)
contact = res.json()Add companies to link the contact to their company, plan and revenue.
5. Page through lists
List endpoints return up to limit items (1–100, default 10) and a nextCursor. Pass that value as cursor to get the next page. When nextCursor is null, you have everything.
async function listAllPosts() {
const posts = [];
let cursor;
do {
const url = new URL("https://do.featurebase.app/v2/posts");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${process.env.FEATUREBASE_API_KEY}`,
"Featurebase-Version": "2026-08-19.orbit",
},
});
const page = await res.json();
posts.push(...page.data);
cursor = page.nextCursor;
} while (cursor);
return posts;
}6. Handle errors
Errors use standard HTTP status codes and always return the same shape:
{
"error": {
"type": "invalid_request_error",
"code": "resource_not_found",
"message": "Post not found",
"param": "id",
"status": 404
}
}💡 Only retry 429 and 5xx responses. Other 4xx errors fail the same way again until you change the request.
Next steps
Collect feedback: create requests from your own forms and tools with
POST /v2/posts. See Collecting feedback through the API for the intake modes.Get notified: create a webhook to receive an HTTP callback when posts, comments, conversations and more change.
Write rich content: article and changelog bodies accept HTML or Markdown, plus Featurebase blocks such as callouts, code tabs and accordions. See the Content Components section of this reference.
Use AI agents: connect any MCP client to
https://mcp.featurebase.appto work with Featurebase in plain language.Explore every endpoint in the sidebar of this API reference.
Still need help? Ask the team