API & Agents

Versioned JSON API reference and agent onboarding workflow for RailsPress 1.3.x.

Overview

RailsPress exposes a versioned JSON API under /railspress/api/v1 for programmatic publishing workflows.

The API is opt-in. Enable it in your initializer before calling endpoints:

config/initializers/railspress.rb
Railspress.configure do |config|
  config.enable_api
  config.current_api_actor_method = :current_user
end

Default v1 scope includes posts, imports, categories, and tags. API keys currently have full access to exposed v1 resources.

Authentication

RailsPress API uses bearer tokens managed from Agents & API in admin (/railspress/admin/api_keys).

Enable API auth wiring in your initializer:

config/initializers/railspress.rb
Railspress.configure do |config|
  config.enable_api
  # Optional: include a host concern into Railspress::Admin::BaseController
  # so current_user and auth guards are available in RailsPress admin.
  # config.admin_auth_concern = "RailspressAdminAuth"
  config.current_api_actor_method = :current_user
  # Alternative when auth relies on request/session helpers:
  # config.current_api_actor_proc = -> {
  #   user = Session.find_by(id: cookies.signed[:session_id])&.user
  #   user if user&.admin?
  # }
  # config.public_base_url = "https://blog.example.com"
end

If current_user is already available in RailsPress admin (Devise-style or via config.admin_auth_concern), current_api_actor_method = :current_user is the clean default.

Use current_api_actor_proc when your auth helper is not directly available on Railspress::Admin::BaseController.

Generated admin API/agent instruction snippets resolve base URL in this order:

  1. config.public_base_url (if set)
  2. Rails.application.routes.default_url_options
  3. Current request base URL

Route Constraints

If your host app mounts RailsPress behind an admin route constraint, allow API paths through the constraint. Otherwise agents will receive 404 before RailsPress API auth runs.

config/routes.rb
class AdminConstraint
  def self.matches?(request)
    return true if request.path == "/railspress/api" || request.path.start_with?("/railspress/api/")

    session_id = request.cookie_jar.signed[:session_id]
    return false if session_id.blank?

    Session.includes(:user).find_by(id: session_id)&.user&.admin?
  end
end

Rails.application.routes.draw do
  constraints AdminConstraint do
    mount Railspress::Engine => "/railspress"
  end
end

Expected behavior:

  • /railspress/admin/* remains host-auth protected.
  • /railspress/api/* reaches RailsPress and then enforces bearer token auth (401 without a token).

Two token types are available:

  • API key (rp_*) for direct content endpoint access.
  • Agent bootstrap key (rpb_*) for one-time exchange at POST /railspress/api/v1/agent_keys/exchange.

API and bootstrap secrets are encrypted with Active Record Encryption. Configure encryption keys in your app before using key management.

Prime Endpoint

Use prime as your first handshake after setting a bearer token. It confirms auth, capabilities, endpoint paths, and default publishing behavior.

Terminal
$ curl -H "Authorization: Bearer $RAILSPRESS_TOKEN" \
  http://localhost:3000/railspress/api/v1/prime

Prime reports that post creation defaults to draft unless post.status is explicitly set to published.

Posts API

Base path: /railspress/api/v1/posts

Endpoints:

  • GET /posts, GET /posts/:id
  • POST /posts, PATCH /posts/:id, DELETE /posts/:id
  • Nested header image, focal point, and context override endpoints when post images/focal points are enabled.

Create draft (default):

Terminal
$ curl -X POST \
  -H "Authorization: Bearer $RAILSPRESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"post":{"title":"API draft","content":"<p>Hello</p>"}}' \
  http://localhost:3000/railspress/api/v1/posts

To publish on create/update, set post.status to published (and optionally post.published_at).

Post Imports API

Base path: /railspress/api/v1/posts/imports

Supported file extensions: .md, .markdown, .txt, .zip.

Submit import, then poll status:

Terminal
$ curl -X POST \
  -H "Authorization: Bearer $RAILSPRESS_TOKEN" \
  -F "file=@./my-posts.zip" \
  http://localhost:3000/railspress/api/v1/posts/imports

$ curl -H "Authorization: Bearer $RAILSPRESS_TOKEN" \
  http://localhost:3000/railspress/api/v1/posts/imports/12

Categories & Tags API

Taxonomy endpoints are available at:

  • /railspress/api/v1/categories (index/show/create/update/destroy)
  • /railspress/api/v1/tags (index/show/create/update/destroy)

Category deletion returns 422 if posts are still assigned. Tag deletion returns 204 when successful.

Admin Key Lifecycle

Manage keys from Agents & API:

  • Create direct API keys (optional expiration, rotate, revoke).
  • Create one-time bootstrap keys (default 1-hour expiration, revoke if unused).
  • Copy generated quick-start instructions from one-time reveal screens.
Security

Plaintext tokens are shown once at create/rotate/exchange time. Store immediately and rotate/revoke when needed.

Errors

All API errors return an error object. Validation failures include a details array.

Status Meaning
401 Unauthorized Missing/invalid/revoked/expired token
404 Not Found Resource missing or API disabled
422 Unprocessable Content Validation failure or unsupported input