What it does
avo-api
An AI agent, a partner integration, a mobile client, a headless front-end:
each one needs the same thing before it can do anything useful, a way to read
and write your data without handing over the keys to everything. You get that
on day one. Run the install generator and every Avo resource
becomes a documented REST API over plain JSON, with a full set of endpoints,
GET,
POST,
PATCH,
PUT, and
DELETE
under /api/resources/v1/<resource>.
Each endpoint reuses your existing resource definition, so the fields, the
pagination, and the sorting all match the admin you already built. No
serializers to write, no routes to wire up.
What makes it safe to point an LLM tool or an outside client at your database
is that the API answers to the rules you already wrote. Your Pundit policies
run on every request, so a caller can only read and change what its user is
allowed to, and field visibility follows the same
:index,
:show,
:new, and
:edit
rules your resources declare. Authentication is yours to set: a generated
setup_authentication hook lets you check an API key,
HTTP Basic credentials, or whatever your app already uses, and anything that
fails comes back as a clean 401. Every client goes
through the same authorization layer as the UI, so what a role cannot see in
Avo it cannot read through the API either.
It also works with the Avo CLI, so you, your scripts, and your coding agents can query and update records from the terminal without writing a single HTTP request.
What it looks like
Add the gem and run one generator. It creates a controller per resource,
inheriting a BaseResourcesController you own:
# Gemfile
gem "avo-api", source: "https://packager.dev/avo-hq/"
$ bundle install
$ rails generate avo_api:install
The API exposes exactly the fields your resource already shows, per view.
hide_on and only_on carry
straight over, so an internal note stays out of the index payload:
class Avo::Resources::Team < Avo::BaseResource
def fields
field :id, as: :id
field :name, as: :text
field :url, as: :text
field :internal_notes, as: :text, hide_on: :index
field :logo, as: :external_image, only_on: :show
end
end
Authorization is your existing Pundit policy, enforced on every request, the same one guarding your admin. Authentication is a hook you fill in, here a shared API key:
class Avo::Api::Resources::V1::UsersController < BaseResourcesController
def setup_authentication
expected = ENV.fetch("API_KEY")
provided = request.headers["Authorization"]&.sub(/^ApiKey /, "")
unless ActiveSupport::SecurityUtils.secure_compare(provided.to_s, expected)
raise Avo::Api::AuthenticationError
end
end
end
From the caller's side it is plain JSON over the verbs you expect:
$ curl -X POST http://localhost:3000/api/resources/v1/teams \
-H "Authorization: ApiKey $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "team": { "name": "Platform", "url": "https://platform.dev" } }'
# 201 Created
# { "record": { "id": 3, "name": "Platform", "url": "https://platform.dev" } }
Work with your data from the terminal
Once the API is running, you and your agents can read and change records from any terminal with the Avo CLI, the API's command-line client, free with this add-on. It talks to your app over HTTP, so nothing extra gets installed in the app, and every call goes through the same policies as the UI. Install it once and log in with an API token:
$ gem install avo-cli
$ avo login --host https://admin.example.com/api --token avo_xxxx
# List your resources, then inspect one's fields
$ avo schema
$ avo schema users
List, read, create, update, and delete records with one command each. Results print as a readable table by default:
$ avo list users --sort name --dir desc --fields id,name,email --per-page 10
$ avo create users --data '{"name": "Ada Lovelace", "email": "ada@example.com"}'
$ avo update users 5 --data '{"name": "Ada King"}'
$ avo delete users 5
Add --format json to script it. Exit codes tell a
refused request (1) apart from a malformed command
(2), so a CI job or an agent knows what went wrong:
$ avo get users 5 --format json | jq .record.email
"ada@example.com"
# Or read the connection from the environment in CI
$ AVO_API_HOST=https://admin.example.com/api AVO_API_TOKEN=$TOKEN avo list orders
What you get
-
Automatic REST endpoints for every Avo resource, with
index,show,create,update, anddestroy -
Field visibility that mirrors your resource views, so the API exposes exactly what
:indexand:showalready show - Your Pundit policies enforced on every request, the same authorization that guards your Avo admin
-
A generated
setup_authenticationhook for an API key, HTTP Basic, or your own scheme, with failures returned as401 -
A
BaseResourcesControllerin your app that you can override per resource when you need to - JSON request and response shapes that match your records, with validation errors returned as 422s
-
A command-line client,
avo-cli, to list, read, create, update, and delete records from any terminal, as tables or JSON
Why it pays off
- Give an AI agent, a partner, or a new client a documented API to your whole admin in an afternoon, instead of writing serializers and routes resource by resource.
- One authorization layer, every surface: the policy that scopes records, hides attributes, and gates actions in the UI does the same for every API caller, so there is no second permission model to write.
- Your data never leaves your Rails app. The API runs inside your codebase, so there is no third-party service sitting between an integration and your database.
- Every Avo release makes it better: the endpoints, serialization, and field handling stay aligned with your resource definitions and keep working through upgrades, and fixes ship to every app at once.
- Your agent writes the authentication hook; the endpoints, serialization, and access control come with the foundation. No per-seat pricing as the clients calling it grow.
Questions
How do API clients authenticate?
By default with API tokens you create in your Avo app, sent as a bearer token in the Authorization header over HTTPS. Tokens can expire and be revoked, and each one acts as the user who owns it. For HTTP Basic, your own tokens or a shared key, override the setup_authentication hook.
Do my Pundit policies apply to API requests?
Yes, through the Authorization add-on. Index results go through the resource's policy scope, a record outside it returns 404, and a denied policy method returns 403. Without the Authorization add-on configured, API queries aren't scoped, so set it up before you hand out tokens.
Is there an OpenAPI spec?
Yes. GET /api/resources/v1/_openapi returns an OpenAPI 3.1 document built on each request from your resources and the caller's token, so every client sees only what it can reach. Point a client generator or an LLM tool at it.