favst

For developers

Build on what people already love.

A REST API over every Favist resource, personal access tokens that inherit exactly your own permissions, a resolver that turns any link into a structured item, and a tool catalog an AI assistant can call directly.

One request to see it work

Create a token in your settings, then ask for your collections. Base URL is https://favist.ai/v1.

curl https://favist.ai/v1/collections \
  -H "Authorization: Bearer $FAVIST_TOKEN"

The one endpoint worth building against first is the resolver. Give it a URL or a plain phrase and it hands back a structured item with artwork, creator and platform links, across music, film, books, articles, places and products.

curl -X POST https://favist.ai/v1/resolve \
  -H "Authorization: Bearer $FAVIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"input": "https://open.spotify.com/track/..."}'

How it is built

A token is a person, not a permission

A token never grants access on its own. It resolves to the member who created it, and everything after that runs under row-level security in the database. A token cannot reach anything you could not reach yourself by logging in.

Declared once, served everywhere

Resources, fields, scopes and verbs live in a single registry. The REST routes, the assistant tool catalog and this page are all generated from it, so a capability arrives on all three surfaces together or on none.

Errors come back, never swallowed

A refused write and a successful one are told apart. When the database refuses something, you get its reason rather than a cheerful empty response.

Additive within a version

Fields get added. Fields do not change meaning. Anything that would break a working integration waits for the next version.

Resources

10 resources, each with the verbs listed. Omitted verbs are omitted on purpose rather than missing.

PathWhat it isVerbs
/v1/collectionsA named group of favorites, public, unlisted or private.list, get, create, update, delete
/v1/itemsOne favorite inside a collection. The join carries per-owner overrides.list, get, create, update, delete
/v1/todosA task, one-off or recurring, with optional reminders.list, get, create, update, delete
/v1/goalsSomething being worked toward, with progress.list, get, create, update, delete
/v1/streaksA habit, with its check-in history.list, get, create, update, delete
/v1/messagesA direct message. Blocks and attachment consent apply.list, get, create
/v1/followsWho you follow. Blocked pairs are refused by policy.list, create, delete
/v1/channelsA public destination of its own, separate from the member's profile.list, get, create, update
/v1/collection-typesWhich of the collection types this member has switched on.list, update
/v1/profileThe authenticated member's own profile.get, update

Beyond CRUD

POST/resolve

Turn a URL or a search phrase into a structured catalog item.

Returns a song_id usable directly when creating an item. Rate limited harder than the rest, since each call may reach an upstream provider.

GET/search

Search the catalog by name across every collection type.

POST/streaks/:id/check-in

Record today's check-in for a streak.

A verb, not a row: check-ins are append-only and the streak's own state is derived, so exposing the table would invite writes that corrupt the count.

POST/uploads

Mint a short-lived presigned URL to upload media directly.

Bytes never pass through this API. Pro only, and the plan is checked against the token's own member.

Conventions

Uniform across every endpoint, because integration platforms depend on that far more than on any single endpoint being clever.

Pagination
Cursor, never offset. Pass ?after= with ?limit= up to 200, default 50.
Idempotency
Send Idempotency-Key on any create. A retry within 24 hours returns the original result rather than a duplicate.
Rate limit
120 requests per token per minute, with a burst of 30. The resolver is tighter, since each call may reach a provider upstream.
Uploads
Presigned. POST /v1/uploads for a short-lived URL and send the bytes straight there. They never pass through the API.
Compatibility
Additive only within a version. A field may be added. No field changes meaning underneath you.
Scopes
16 of them, coarse on purpose. Per-field scopes read well in a design document and are unusable in a consent screen.
Planned

Webhooks and integration platforms

The resources that will fire triggers are already declared, signed with HMAC-SHA256 on X-Favist-Signature, with at-least-once delivery. Delivery itself is not shipped yet, and neither are the connectors that would sit on top of it, so this is written here as a plan rather than as something you can build against today.

Scopes

collections:readcollections:writegoals:readgoals:writeitems:readitems:writemessages:readmessages:writeprofile:readprofile:writesocial:readsocial:writestreaks:readstreaks:writetodos:readtodos:write

Let an assistant use it

Favist publishes its capabilities as a tool catalog, so an AI assistant can add a favorite, search a collection or build a list on someone's behalf. It is the same set of tools Fave uses inside Favist, which means nothing is quietly available in here and missing out there.

# The catalog: every capability as a callable tool definition
curl https://favist.ai/api/agent/tools \
  -H "Authorization: Bearer $FAVIST_TOKEN"

# Run one
curl -X POST https://favist.ai/api/agent/invoke \
  -H "Authorization: Bearer $FAVIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tool": "search", "input": {"query": "radiohead"}}'

A handful of tools return a directive for the browser to carry out rather than running on the server, because playing a song or listening on the microphone has to happen where the person is.

Get a token and try the resolver.

It is the endpoint that is genuinely hard to replicate, and it takes one request to see why.