Outcome
Create an Auth-owned workspace and its Socialize profile, then reconcile both sides before retrying an uncertain result.
Action: app_topolo_socialize.brands.create.
Before you start
- Verify the original principal and intended organization. Use supported workspace discovery to check the actual caller’s creation eligibility for Socialize; Auth makes the final runtime decision. Organization ownership or an app permission alone does not prove creation is allowed.
- Published permission is app_topolo_socialize.workspace:write. Review the requested name, optional description/default choice and both Auth/local-profile effects; validate and plan the actual request before confirming.
- The route requires authenticated original user id/email, organization and authorization context. Do not forge X-Auth headers or assume an API-key-only principal supplies every field this route needs.
Discover the creation boundary
- Use supported workspaces.list for Socialize to inspect current workspaces and caller creation policy before a mutation; retain the before-list to reconcile uncertainty.
- This is a new workspace, so no new id may be guessed as a resource binding. Follow the actual creation plan’s context; after success, use data.id and supported discovery to resolve the new workspace.
Request
POST /api/brands
| Field | Required | Meaning |
|---|---|---|
| name | Yes | 1–100 characters before source-route trimming. Auth owns the resulting workspace name and applies its own policy. |
| description | No | Up to500characters, stored only in Socialize. |
| isDefault | No | Explicit requested default selection forwarded to Auth. Omit to defer to Auth policy; verify actual outcome. |
Do not add appId, organizationId, workspaceId, creator or idempotency fields to this action input. Current schema rejects undeclared fields.
Effects
- Delegates canonical workspace creation to Auth with resolved Socialize app id, original organization, trimmed name and isDefault only when supplied. Description stays Socialize-owned; it is not sent as Auth workspace identity.
- After Auth success, synchronizes the local brand mirror and optionally updates its description. Auth creation, mirror synchronization and local description update are not shown as one transaction. A later502/500 or interrupted response can leave a canonical workspace and partial local state.
- The returned memberCount is hardcoded0 and logoUrl empty on this path, not an authoritative query of grants or existing memberships. Auth owns the name, slug, owner and actual default status; these are not included in this response.
- When an Idempotency-Key HTTP header is already supplied by a supported transport, this handler forwards it to Auth. The action input schema has no idempotencyKey field; do not invent a CLI/MCP option or assume automatic replay protection. Existing-workspace reconciliation remains mandatory after uncertainty.
Interpret the response
HTTP201 returns {data:{...}}. data.id is the new canonical workspace id, not a local brand alias. The response contains local description/logo/memberCount, organizationId and timestamps; it omits workspace name, slug, owner and default flag. Auth numeric createdAt values are normalized to ISO time by this route; strings are preserved. Placeholder timestamps below illustrate shape, not live execution.
Worked examples
All examples are source-derived, synthetic and unrun. They do not authorize creating a real workspace.
Create a reviewed editorial workspace
Source-derived synthetic and unrun. Assumes Auth creation policy allows the original caller’s reviewed request and returns the illustrated workspace identity. No real workspace was created.
{
"name": "Example editorial",
"description": "Example editorial workspace",
"isDefault": false
}
Illustrative HTTP201 body:
{
"data": {
"id": "ws_socialize_example",
"description": "Example editorial workspace",
"logoUrl": "",
"memberCount": 0,
"organizationId": "org_example",
"createdAt": "2026-10-21T00:00:00.000Z",
"updatedAt": "2026-10-21T00:00:01.000Z"
}
}
Verify data.id in Auth-owned workspace discovery, then its Socialize profile. Verify actual default status separately; it is not returned here.
Create with no optional profile fields
Source-derived synthetic and unrun. isDefault is omitted rather than fabricated; default selection remains Auth policy.
{
"name": "Example team"
}
Illustrative HTTP201 body:
{
"data": {
"id": "ws_socialize_other_example",
"description": "",
"logoUrl": "",
"memberCount": 0,
"organizationId": "org_example",
"createdAt": "2026-10-21T01:00:00.000Z",
"updatedAt": "2026-10-21T01:00:01.000Z"
}
}
Read the actual created workspace and default state; the empty local description/logo and zero reported memberCount do not establish an empty or deletable Auth workspace.
Refusals and recovery
| Status | Condition | Next step |
|---|---|---|
| 400 | invalid_json | Correct malformed JSON before a newly reviewed call; do not assume the earlier uncertain request had no effect. |
| 400 | validation_error | Inspect flattened route validation issues for name/description/isDefault. Name is length checked before trimming and Auth applies its own validation; do not rely on whitespace-only names. |
| 400 | organization_required | Use the original authorized organizational context, without inventing organizationId input or changing the shared default. |
| 401 | unauthorized | The required authorization context is absent. Authenticate through the supported flow and reverify the actual principal. |
| 502 | workspace_creation_failed | Auth success lacked an expected returned workspace id. Reconcile existing workspaces before any repeat; do not assume creation was rolled back. |
| 500 | failed_to_create_brand | Preserve request evidence and discover Auth/local state. This can follow Auth creation or local writes; do not replay blindly. |
Auth non-success responses are forwarded with their actual status/payload before local mirror creation. Do not replace them with invented common409/403 carrier examples or infer Auth’s effect state from an HTTP code alone. ApplicationError branches retain their problem-detail form; generic exceptions use the source-specific failed_to_create_brand response. Outer gateway/authentication refusals can differ.
No transactional rollback is guaranteed across Auth workspace creation and local profile synchronization. Preserve before-state and discover the exact created id after uncertainty before a separately authorized repair. The published compensating action points to workspaces.delete, whose app path asks Auth to archive only after local emptiness checks. Its current policy, default-workspace restriction, actual transport and user approval must be qualified separately; do not automatically delete/archive or invent a rollback for a partially created/default workspace.
Verify the result
- Retain data.id, then verify that exact canonical workspace through supported workspace discovery in the original organization, including actual name/default status. A successful app response alone is not a complete Auth policy proof.
- Use brands.get for that workspace’s local profile and compare its organization and intended description. brands.list is cached and can reconcile memberships; do not rely on a stale list alone or invoke a tight polling loop.
- After an uncertain result, discover the original intended workspace before another create. Reconcile Auth identity and local profile separately; a valid name is not a unique id or a safe deduplication key by itself.
Related actions
Change log
2026-10-21: Added source-qualified creation guidance, canonical Auth/local-profile separation, policy/default boundaries, two synthetic unrun worked examples and partial-write reconciliation. Reviewed source f8e30bad6947f868302f24e9cb31b6a2bdd1fbf6; current declaration marker bc22ce7a2ceaa4396388794c4d42895c777bed76. No deployed serving-source association, business execution or provider/test result is claimed.