Appearance
Fonts
Font sources, and the font families a library declares.
GET /v1/fonts/sources
The font sources.
The presets (Google Fonts, Fontsource), then the declared sources in the order they were added, each on or off. Needs library.write in an environment, or fonts.sources; only fonts.sources sees which secret a token refers to.
Responses
| Status | Description | Body |
|---|---|---|
200 | The sources | object |
403 | The caller has neither library.write nor fonts.sources | |
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
200 body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
sources | array of FontSource | yes |
POST /v1/fonts/sources
Declare a font source.
Needs fonts.sources, which agents never have. The id is the one given, else one made from the name. A token is a secret: reference, never a token. Audited as fonts.source.add <id>.
Request body (application/json): FontSourceEdit
Responses
| Status | Description | Body |
|---|---|---|
201 | The source | FontSource |
403 | The caller lacks fonts.sources | |
409 | {error: taken}: a source has the id given | |
422 | ||
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
PUT /v1/fonts/sources/{id}
Change a font source, or turn it on or off.
Needs fonts.sources. A preset takes only enabled, its ref and token (Google Fonts) and maxBytes. Audited as fonts.source.change <id>.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes |
Request body (application/json): FontSourceEdit
Responses
| Status | Description | Body |
|---|---|---|
200 | The source | FontSource |
403 | The caller lacks fonts.sources | |
404 | No source has the id | |
422 | ||
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
DELETE /v1/fonts/sources/{id}
Remove a declared font source.
Needs fonts.sources. Presets can't be removed (409 {error: preset}). Families imported from the source stay in their libraries. Audited as fonts.source.remove <id>.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes |
Responses
| Status | Description | Body |
|---|---|---|
204 | Removed | |
403 | The caller lacks fonts.sources | |
404 | No source has the id | |
409 | {error: preset}: a preset can't be removed | |
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
POST /v1/fonts/sources/{id}/test
Fetch a font source's catalogue now, to see that it works.
Needs fonts.sources; works on a source that is off. A url source has no catalogue: with {url} it reads that link. Errors are as for the catalogue routes.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes |
Request body, optional (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
url | string (uri) |
Responses
| Status | Description | Body |
|---|---|---|
200 | It answered | object |
403 | The caller lacks fonts.sources | |
404 | No source has the id | |
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
502 | The source failed: | |
503 | {error: rate-limited}: GitHub's rate limit was reached; give the source a token |
200 body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
ok | boolean | yes | |
families | integer | yes | |
message | string | yes |
GET /v1/fonts/sources/{id}/families
Search a font source's families, or read a link.
Needs library.write and the source on. GitHub tree listings and Fontsource's list are cached for a day. For a url source, ?url= reads the link (a font file or a zip) and answers each family it holds with its detail.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | |
q | query | string | ||
url | query | string (uri) |
Responses
| Status | Description | Body |
|---|---|---|
200 | At most 60 families, best matches first; with url, the link's families | object |
400 | {error: no-catalogue}: a url source takes ?url= | |
403 | The caller lacks library.write | |
404 | No source has the id | |
409 | ||
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
502 | The source failed: | |
503 |
200 body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
families | array of one of FontFamily, FontFamilyDetail | yes |
GET /v1/fonts/sources/{id}/families/{family}
A family's styles, documents and licence.
Needs library.write and the source on. Static files are offered per style where the source has them, else the variable file's weights.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | |
family | path | string | yes | The family's id in its source; a folder path keeps its slashes |
Responses
| Status | Description | Body |
|---|---|---|
200 | The family | FontFamilyDetail |
403 | The caller lacks library.write | |
404 | No such source or family | |
409 | ||
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
502 | The source failed |
GET /v1/fonts/sources/{id}/preview
A family's font file, to draw its name in.
Needs library.write and the source on. One of the family's files (its regular where it has one), at most 4 MB, served as a font with Content-Security-Policy default-src 'none'.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | |
family | query | string | yes |
Responses
| Status | Description | Body |
|---|---|---|
200 | The font | |
403 | The caller lacks library.write | |
404 | No such source or family | |
409 | ||
413 | The file is larger than a preview may be | |
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
POST /v1/libraries/{name}/fonts
Import a font family into the library's draft.
Needs library.write in the environment and the source on. Fetches the styles picked (a static file per style where the source has one, else the variable file covering them) and the family's documents (licence, readme, notes) into the draft under fonts/<family>/, and declares the family in the manifest's fonts with where it came from and its version. A family the library has from the same source is added to: new styles are fetched, existing ones kept. A WOFF is stored as the TTF or OTF it holds. Every file is checked to be a font. Audited as library.font.add <family>.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
name | path | string | yes |
Request body (application/json): FontImport
Responses
| Status | Description | Body |
|---|---|---|
200 | The family as the library now declares it | FontDeclared |
400 | A url source without a link, or no family named | |
403 | The caller lacks library.write | |
404 | No such library, source or family | |
409 | {error: source-off}, {error: family-exists} (declared from elsewhere), or {error: several-families, families} for a zip holding more than one | |
413 | {error: too-large}: over the source's cap | |
422 | {error: no-styles} or | |
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
502 | The source failed |
PUT /v1/libraries/{name}/fonts/{family}
Declare font files already in the draft as a family.
For fonts uploaded by hand: the files (in the draft) with their weights and styles, and any documents. A family imported from a source keeps where it came from. Needs library.write. Audited as library.font.declare <family>.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
name | path | string | yes | |
family | path | string | yes |
Request body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
environment | string | ||
files | array of LibraryFontFile | yes | |
documents | array of LibraryFontDocument |
Responses
| Status | Description | Body |
|---|---|---|
200 | The family as the library now declares it | FontDeclared |
400 | The draft would fail its check (a file it doesn't have, one that isn't a font, a bad weight or style) | |
403 | The caller lacks library.write | |
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
DELETE /v1/libraries/{name}/fonts/{family}
Remove a declared family, with its files and documents.
Needs library.write. Audited as library.font.remove <family>.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
name | path | string | yes | |
family | path | string | yes | |
environment | query | string |
Responses
| Status | Description | Body |
|---|---|---|
200 | Removed | object |
403 | The caller lacks library.write | |
404 | The library doesn't declare the family | |
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
200 body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
family | string | ||
commit | string |
GET /v1/libraries/{name}/fonts/updates
Check the library's imported font families for newer versions.
Only when asked: nothing checks in the background. Each family the draft (else the latest release) declares from a source is compared with the source now: GitHub's blob SHAs at the source's ref, Fontsource's release, the index's version and links, or a link's ETag, Last-Modified or sha256. Families uploaded by hand are left out. Needs library.write.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
name | path | string | yes | |
environment | query | string |
Responses
| Status | Description | Body |
|---|---|---|
200 | Each sourced family | object |
403 | The caller lacks library.write | |
404 | No such library | |
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
200 body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
families | array of object | yes | |
families[].family | string | yes | |
families[].source | string | yes | |
families[].status | current, newer, gone, unavailable | yes | unavailable: the source is off, removed or failed (message). |
families[].from | string | ||
families[].to | string | ||
families[].changes | array of string | yes | |
families[].message | string |
POST /v1/libraries/{name}/fonts/{family}/update
Update an imported font family from its source.
Fetches the family's styles again into the draft, replacing its files and documents (adding new ones and removing those the source no longer has), and moves its version. It ships when the library is released, whose notes say "<family> updated from <source> @ <version>". Needs library.write and the source on. Audited as library.font.update <family>.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
name | path | string | yes | |
family | path | string | yes |
Request body, optional (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
environment | string |
Responses
| Status | Description | Body |
|---|---|---|
200 | The family as the library now declares it; warnings name styles the source no longer has | FontDeclared |
403 | The caller lacks library.write | |
404 | The library doesn't declare the family, or the source hasn't it | |
409 | {error: no-source} for fonts uploaded by hand, or | |
429 | The request's class is over its rate limit (limits.rate, or the API key's own rateLimits): {error: "rate-limited", class, message, retryAfter}. The MCP tool-call and agent edit limits answer the same body without class. | object |
502 | The source failed |