Skip to main content

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-specwebsite/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).

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.