Files
ignacionelson ddf09677f0 Document the branding endpoints where their callers are
Branding moved into the application on 2026-08-28 and its two read-only
endpoints came with it unchanged -- same paths under
/api/v1/modules/branding, same capability, same ability. Their
documentation did not: the guide still sent readers to
packages/cloud-modules/docs/api.md, so endpoints that every installation
now carries were described in a private repository almost none of their
callers can open.

The OpenAPI document is still not the place for them. OpenApiContractTest
skips api/v1/modules/* on purpose: that document is served
unauthenticated and has to be identical on every installation, while a
module's paths exist only where the module does. So this is a plain
markdown file beside the guide, and published like it -- ignored docs are
maintainer notes, and this one is for integrators.

It is the cloud-modules file moved across, minus the attribution switch:
that half stayed Cloud-only and has no API surface at all. The gate is
described as every edition holding branding.customize, with a hosted plan
able to subtract it, because that is what the enum now says.
2026-09-02 18:56:55 -03:00

83 lines
3.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# API — module endpoints
Optional modules add endpoints under `/api/v1/modules/{module}/…`. They are **not** in the committed
[`api/openapi.json`](api/openapi.json): `OpenApiContractTest` skips `api/v1/modules/*`, because that
document is served unauthenticated and has to be identical on every installation, while a module's
paths exist only where the module does. This file is the documentation for the modules that ship
with the application; a module living in its own package documents its surface in its own repository.
Everything [`api-guide.md`](api-guide.md) describes — bearer-token authentication, ability checks,
RFC 7807 errors, rate limits — applies unchanged. The core supplies all of it; none of it is
restated per module.
`GET /api/v1/me` lists the modules an installation actually carries, so an integration can check for
`branding` before calling anything below rather than guessing from a 404.
---
## Branding
Mounted at `/api/v1/modules/branding`, behind `capability:branding.customize`. Every edition has
that capability; a hosted plan can have it subtracted from its environment, in which case these
paths answer 403 like any other gated route.
Both endpoints are read-only. Uploading either image is a multipart flow whose content-sniffing
rules only make sense behind a file picker, and settings writes follow the rule that there is never
a generic `PATCH /settings`.
Hiding the attribution line is not here. That switch is Cloud-only, has no API surface, and its
column is written by the `cloud-modules` package.
### `GET /logo` — ability: `edit_settings`
The logo shown in place of the default sidebar icon.
```json
{
"data": {
"logo_url": "https://example.test/storage/branding/9f3c….png",
"updated_at": "2026-08-07T16:02:30+00:00"
}
}
```
`logo_url` is `null` when no logo has been uploaded, which is the normal state rather than an error.
### `GET /watermark` — ability: `edit_settings`
The mark stamped onto the thumbnails and previews clients and anonymous public visitors see. What
this installation's own staff see goes unmarked, and the stored files — including every download —
are never altered.
```json
{
"data": {
"enabled": true,
"image_url": "https://example.test/storage/branding/a68a….png",
"position": "bottom-right",
"size": 35,
"opacity": 55
}
}
```
| Field | Meaning |
|---|---|
| `enabled` | Whether client- and public-facing images are *actually* being watermarked. False whenever nothing is drawn — including when the toggle is on but its image has since been removed. It answers "is this installation watermarking?", not "which way is the switch pointing?" What staff see is never marked regardless. |
| `image_url` | The artwork, or `null` if none was ever chosen. |
| `position` | One of `top-left`, `top-center`, `top-right`, `middle-left`, `center`, `middle-right`, `bottom-left`, `bottom-center`, `bottom-right`. |
| `size` | Percentage of the image the mark is scaled to fit inside, keeping its proportions — so a thumbnail and a preview carry the same design at different scales. 5100. |
| `opacity` | Percentage. 1100. |
An installation that has never opened the branding screen answers with the defaults it would start
from (`enabled: false`, `bottom-right`, `30`, `60`) rather than a payload of nulls.
---
## Deferred, on purpose
- **Writes for either image.** See above.
- **Rendered thumbnails themselves.** Already deferred by the host ([`api-todo.md`](api-todo.md));
the watermark endpoint exists so an integration generating its own derivative images can reproduce
the installation's mark, not as a step toward serving thumbnails over the API.