Core concepts
If you will add or change API endpoints, read this first. Others start at Ways to contribute. Step-by-step guides live under Developer documentation.
Permissions and scopes
OAuth scopes live on the token. Permissions (abilities) live on the action. The permission registry is filled when a gem calls ext.permissions inside Extension.register (paired with ext.oauth_scopes when the scope is new). System Admin checkboxes and CLI validation read that registry.
Controllers call doorkeeper_authorize! first, then ability.authorize!.
See also: Scopes and permissions.
Webhooks
Register listeners with ext.webhooks in the engine. That catalog feeds admin UI and OpenAPI.
Domain events enqueue a job; the job delivers signed HTTP POSTs to client registrations (HMAC headers).
See also: Webhooks (dev), Webhooks (integrator).
Doorkeeper protects the API
API clients are Doorkeeper applications. Scopes limit which route families a token may call. Requests carry a Bearer token.
RSwag describe_api_endpoint documents client-credential vs resource-owner flows.
See also: Scopes and permissions, Client credential flow, User credential flow.
OpenAPI: RSwag → swaggerize → openapi-generator → ReDoc
Document endpoints in request specs (RSwag). Do not hand-edit the OpenAPI JSON.
Regenerate with yarn gen:openapi-spec → website/static/openapi.json (via swaggerize). Generate clients with yarn gen:node-client or bin/gen-node-client.
The site ReDoc at /api/ reads that static file. Refresh the docs site after regenerating (yarn docs:build or the publish pipeline).
See also: RSwag, Generate clients, Command-line tools.
Decidim practice: controller → command → form
Keep controllers thin: authorize, call a command or form, render.
Forms validate params. Commands perform side effects and emit result events. Prefer Decidim domain objects—do not invent parallel business logic in the API layer.
See also: Controllers, Async (mutations often enqueue commands).
API design: flat resources, related, meta
Prefer flat collections with filters over nested trees.
Bad: /participatory_processes/1/components/meetings
Good: /components/search?filter[participatory_space_id_eq]=1&filter[participatory_space_type_eq]=Decidim::ParticipatoryProcess (add filter[manifest_name_eq]=meetings when you need a component type)
JSON:API-style: attributes on the resource; associations via relationships / includes (related); non-attribute context in meta.
See also: Space and components, Binding and relations, Filtering and pagination, Serializations.