Upload an avatar image and obtain its URL.
Action: app_topolo_calendar.host.avatar.upload · POST /api/admin/host/avatar
Declared permission: app_topolo_calendar.host:write. For authorized Calendar users; permissions and resource access still apply.
Before you start
- Use the current credential to confirm the production organization and inspect this exact action before preparing a call.
- Declared permission: app_topolo_calendar.host:write. Permission discovery is not a grant.
- Discover the authorized Calendar workspace and pass it as resourceType=workspace/resourceId separately from this action input. Calendar records and IDs are scoped to the selected host/workspace.
- Validate and plan the exact payload, obtain effect-specific authorization, then execute unchanged only in a separately authorized operational task. Do not run these synthetic inputs.
Inputs and field meanings
| Field | Presence | Declared constraint | Meaning |
|---|---|---|---|
contentBase64 | required | string | Base64 image bytes, not a filesystem path; decode must be nonempty and at most 2 MiB (2,097,152 bytes) in the reviewed source. |
contentType | required | string; enum=["image/jpeg", "image/png", "image/webp", "image/gif"] | One declared MIME type: image/jpeg, image/png, image/webp or image/gif. |
fileName | required | string | Nonempty source filename required by the published JSON input; the reviewed upload creates its own storage key. |
Worked request and response
Source-reviewed illustration only. Nothing below was executed; all identifiers, counts, states, credentials and URLs are synthetic. Replace identifiers through discovery before any separately authorized call.
Resource context: workspace; it is not an extra action input field.
Illustrative action input:
{
"contentBase64": "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
"contentType": "image/gif",
"fileName": "illustrative-pixel.gif"
}
Illustrative HTTP 200 source response:
{
"avatarUrl": "https://assets.example.com/illustrative-avatar.gif"
}
avatarUrl is an illustrative upload URL, not a captured deployed asset. Upload does not itself write host.avatarUrl; attach the returned URL through host.update and read the profile.
The illustrative response passes the declared structural schema. This establishes only schema compatibility, not deployed shape, values, authorization or business outcome.
Effects and verification
- Stores an image object in the avatar bucket and returns a public URL.
- Does not by itself update the host profile or prove reader rendering.
- Validate the actual returned URL, attach it with host.update if intended, and read host.get; separately inspect the public image when authorized.
Refusals and recovery
Selected source refusals are listed below; runtime middleware can return additional structured errors.
| HTTP | Error | Supported response |
|---|---|---|
| 403 | forbidden | Reconcile the declared permission, organization and verified resource grant with the legitimate access owner; do not switch context to bypass a refusal. |
| 404 | no_host | Read host.get; if it returns host:null, create the intended workspace host through host.update before retrying. |
| 400 | unsupported_avatar_type | Use one of the declared image MIME types. |
| 400 | avatar_too_large | The decoded bytes must be nonempty and at most 2 MiB (2,097,152 bytes), the current source limit. |
| 400 | invalid_avatar_base64 | Encode the image bytes as valid base64. |
Recovery: No avatar-object delete action is published here. Restore the previous profile URL through host.update if needed; an uploaded object may remain even when unused.
After an uncertain mutation, reconcile the named records and provider/delivery evidence before repeating it.
Related actions
app_topolo_calendar.host.get: Read the selected workspace host, including the unconfigured host:null state.app_topolo_calendar.host.update: Create the first host or replace its editable profile fields.
Declared schemas and provenance
The hidden action guide carries these exact current declared schemas. They are not rewritten to make the illustrations pass.
Input schema:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"contentBase64": {
"type": "string",
"minLength": 1
},
"contentType": {
"type": "string",
"enum": [
"image/jpeg",
"image/png",
"image/webp",
"image/gif"
]
},
"fileName": {
"type": "string",
"minLength": 1
}
},
"required": [
"contentBase64",
"contentType",
"fileName"
],
"additionalProperties": false
}
Output schema:
{
"type": "object",
"title": "Upload host avatar response",
"description": "Operation result returned by host.avatar.upload.",
"properties": {
"success": {
"type": "boolean"
},
"id": {
"type": "string"
},
"data": {
"oneOf": [
{
"type": "object",
"title": "host record",
"properties": {
"id": {
"type": "string",
"description": "Stable identifier when the host exposes one."
}
},
"additionalProperties": true
},
{
"type": "object",
"additionalProperties": true
}
]
},
"result": {
"type": "object",
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"additionalProperties": true,
"x-topolo-contract-source": "derived-action-metadata",
"x-topolo-contract-precision": "structural"
}
Source: TopoloCalendar@b378ceb73800ee1d1e6c4ff6f0b4179918cf632c, apps/api/src/routes/admin-host.ts. Current catalog metadata and source were compared without executing the application.
- Source-reviewed synthetic input/response; not captured or executed.
- Source revision is not proof of current deployment, caller access, provider connection or notification delivery.
- The illustrative response passes the declared structural schema. This establishes only schema compatibility, not deployed shape, values, authorization or business outcome.
- The tiny GIF input is synthetic, schema-checked data, not an executed upload. The reviewed AVATAR_MAX_BYTES limit is 2 MiB (2,097,152 decoded bytes).