Topolo Docs Topolo Platform
Open workspace

Update the job board

Update the job board’s live address, company text and appearance.

Outcome

Update the job board’s live address, company text and appearance. Recruiters only.

  • This changes the live board. Its public pages show a new appearance on their next view; use job_board.draft.update to edit a working draft instead.
  • Only supplied fields change. Supplying a list replaces that list.
  • appearance.source: "brand" follows the organization’s published Brand, with explicit board settings taking precedence. manual ignores Brand. A null field clears that board setting; inspect effectiveAppearance to verify the resulting values.

Before you start

  • Verify identity and the pinned organization context, then discover the exact action and a granted Recruit workspace.
  • The required application permission is workspace:write. Hiring-team roles and record visibility are enforced separately; a workspace grant alone does not grant every Recruit action.
  • Organization and workspace identity come from verified context. Pass the workspace as resourceType/resourceId, separate from action input; request-body organizationId/workspaceId values cannot select an authorized scope.
  • The first authorized action can initialize an identity-only workspace mirror and invoke the configured workspace-created callback. It does not create an Auth workspace or grant new access.

Recruiters can perform the full hiring workflow. Hiring managers see their managed roles and candidates; interviewers see assigned interviews and their candidates. Workspace owners and organization administrators act as recruiters. Use team.me to read the caller's effective role and allowed actions.

Discover the target

topolo whoami --json
topolo actions get app_topolo_recruit.job_board.update --detail --json
topolo resources topolo-recruit --json
topolo workspaces list topolo-recruit --json
topolo actions get app_topolo_recruit.team.me --detail --json

Request

PATCH /api/job-board

Save input as payload.json. CLI and MCP action input includes path identifiers. The reviewed plan separates path/query fields from the HTTP body. Set RECRUIT_WORKSPACE_ID from granted resource discovery.

Inspect and test this action in the signed action console. Use its discovered schema and plan; a test response still requires the verification described below.

Input fields

FieldRequiredTypeConstraints
slugOptionalstring{"pattern":"^a-z0-9?$"}
titleOptionalstring{"minLength":1,"maxLength":160}
introOptionalUnion{}
appearanceOptionalobject{}

Exact input schema:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "pattern": "^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$"
    },
    "title": {
      "type": "string",
      "minLength": 1,
      "maxLength": 160
    },
    "intro": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 4000
        },
        {
          "type": "null"
        }
      ]
    },
    "appearance": {
      "type": "object",
      "properties": {
        "source": {
          "type": "string",
          "enum": [
            "brand",
            "manual"
          ]
        },
        "logoUrl": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 2000,
              "pattern": "^https:\\/\\/[^\\s\"'<>()\\\\]+$"
            },
            {
              "type": "null"
            }
          ]
        },
        "logoReversedUrl": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 2000,
              "pattern": "^https:\\/\\/[^\\s\"'<>()\\\\]+$"
            },
            {
              "type": "null"
            }
          ]
        },
        "primaryColor": {
          "anyOf": [
            {
              "type": "string",
              "pattern": "^#[0-9a-fA-F]{6}$"
            },
            {
              "type": "null"
            }
          ]
        },
        "coverImageUrl": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 2000,
              "pattern": "^https:\\/\\/[^\\s\"'<>()\\\\]+$"
            },
            {
              "type": "null"
            }
          ]
        },
        "headline": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 120
            },
            {
              "type": "null"
            }
          ]
        },
        "font": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "system",
                "grotesk",
                "serif",
                "rounded"
              ]
            },
            {
              "type": "null"
            }
          ]
        },
        "themeMode": {
          "type": "string",
          "enum": [
            "light",
            "dark",
            "auto"
          ]
        },
        "headerLinks": {
          "maxItems": 5,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string",
                "minLength": 1,
                "maxLength": 40
              },
              "url": {
                "type": "string",
                "maxLength": 2000,
                "pattern": "^https:\\/\\/[^\\s\"'<>()\\\\]+$"
              }
            },
            "required": [
              "label",
              "url"
            ],
            "additionalProperties": false
          }
        },
        "siteUrl": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 2000,
              "pattern": "^https:\\/\\/[^\\s\"'<>()\\\\]+$"
            },
            {
              "type": "null"
            }
          ]
        },
        "footerLinks": {
          "maxItems": 8,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string",
                "minLength": 1,
                "maxLength": 60
              },
              "url": {
                "type": "string",
                "maxLength": 2000,
                "pattern": "^https:\\/\\/[^\\s\"'<>()\\\\]+$"
              }
            },
            "required": [
              "label",
              "url"
            ],
            "additionalProperties": false
          }
        },
        "showPoweredBy": {
          "type": "boolean"
        },
        "heroVariant": {
          "type": "string",
          "enum": [
            "text",
            "image"
          ]
        },
        "subheadline": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 280
            },
            {
              "type": "null"
            }
          ]
        },
        "cover": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string",
                  "maxLength": 2000,
                  "pattern": "^https:\\/\\/[^\\s\"'<>()\\\\]+$"
                },
                "alt": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 160
                },
                "focalX": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100
                },
                "focalY": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100
                }
              },
              "required": [
                "url",
                "alt"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ]
        },
        "sections": {
          "type": "object",
          "properties": {
            "about": {
              "type": "object",
              "properties": {
                "enabled": {
                  "type": "boolean"
                },
                "body": {
                  "anyOf": [
                    {
                      "type": "string",
                      "maxLength": 1000
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              },
              "additionalProperties": false
            },
            "whyJoin": {
              "type": "object",
              "properties": {
                "enabled": {
                  "type": "boolean"
                },
                "items": {
                  "maxItems": 6,
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 60
                      },
                      "body": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 240
                      }
                    },
                    "required": [
                      "title",
                      "body"
                    ],
                    "additionalProperties": false
                  }
                }
              },
              "additionalProperties": false
            },
            "benefits": {
              "type": "object",
              "properties": {
                "enabled": {
                  "type": "boolean"
                },
                "items": {
                  "maxItems": 12,
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "label": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 60
                      },
                      "icon": {
                        "type": "string",
                        "enum": [
                          "health",
                          "wellbeing",
                          "time_off",
                          "flexible_hours",
                          "remote",
                          "parental_leave",
                          "family",
                          "learning",
                          "growth",
                          "equity",
                          "pension",
                          "bonus",
                          "equipment",
                          "meals",
                          "travel",
                          "volunteering"
                        ]
                      }
                    },
                    "required": [
                      "label",
                      "icon"
                    ],
                    "additionalProperties": false
                  }
                }
              },
              "additionalProperties": false
            },
            "gallery": {
              "type": "object",
              "properties": {
                "enabled": {
                  "type": "boolean"
                },
                "images": {
                  "maxItems": 6,
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "url": {
                        "type": "string",
                        "maxLength": 2000,
                        "pattern": "^https:\\/\\/[^\\s\"'<>()\\\\]+$"
                      },
                      "alt": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 160
                      },
                      "focalX": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100
                      },
                      "focalY": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100
                      }
                    },
                    "required": [
                      "url",
                      "alt"
                    ],
                    "additionalProperties": false
                  }
                }
              },
              "additionalProperties": false
            },
            "hiringProcess": {
              "type": "object",
              "properties": {
                "enabled": {
                  "type": "boolean"
                },
                "steps": {
                  "maxItems": 6,
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 60
                      },
                      "body": {
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 200
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "title"
                    ],
                    "additionalProperties": false
                  }
                }
              },
              "additionalProperties": false
            }
          },
          "additionalProperties": false
        },
        "rolesList": {
          "type": "object",
          "properties": {
            "groupBy": {
              "type": "string",
              "enum": [
                "none",
                "team",
                "location"
              ]
            },
            "filters": {
              "maxItems": 4,
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "team",
                  "location",
                  "workplace",
                  "type"
                ]
              }
            },
            "sort": {
              "type": "string",
              "enum": [
                "newest",
                "title"
              ]
            }
          },
          "additionalProperties": false
        },
        "confirmationMessage": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 500
            },
            {
              "type": "null"
            }
          ]
        },
        "seo": {
          "type": "object",
          "properties": {
            "metaTitle": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 70
                },
                {
                  "type": "null"
                }
              ]
            },
            "metaDescription": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 160
                },
                {
                  "type": "null"
                }
              ]
            },
            "shareImage": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "maxLength": 2000,
                      "pattern": "^https:\\/\\/[^\\s\"'<>()\\\\]+$"
                    },
                    "alt": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    }
                  },
                  "required": [
                    "url",
                    "alt"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            }
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    }
  },
  "additionalProperties": false
}

Appearance controls

  • Identity and theme: source, logoUrl, logoReversedUrl, primaryColor, font and themeMode.
  • Navigation: headerLinks, siteUrl, footerLinks and showPoweredBy.
  • Hero: heroVariant, headline, subheadline, coverImageUrl and cover. The structured cover requires a URL and alt text, with optional focal coordinates.
  • Optional content: sections.about, whyJoin, benefits, gallery and hiringProcess. Each section has an enabled switch; their order is fixed.
  • Roles and completion: rolesList controls grouping, filters and sorting; confirmationMessage customizes the application confirmation.
  • Search and sharing: seo.metaTitle, metaDescription and shareImage.

Use the complete nested input schema returned by the action-discovery command above, or MCP topolo_get_action, for exact allowed values and limits. Do not infer additional fields from this summary.

Effects

  • Updates the job board fields; a published board changes address immediately when the slug changes, and its public pages show a new appearance on their next view.

Interpret the response

Call job_board.get and confirm the slug, title, intro and appearance, and that effectiveAppearance shows what the pages should render.

The job board uses the verified workspace. A nullable slug is a public address, not a canonical Topolo resource identifier.

Exact success schema:

{
  "type": "object",
  "required": [
    "board"
  ],
  "properties": {
    "board": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "string",
            "null"
          ],
          "description": "The board's stable id (board_…): the {boardId} of the public careers API and the id of its recruit.board reference. Null until the board is first saved."
        },
        "slug": {
          "type": [
            "string",
            "null"
          ]
        },
        "title": {
          "type": [
            "string",
            "null"
          ]
        },
        "intro": {
          "type": [
            "string",
            "null"
          ]
        },
        "published": {
          "type": "boolean"
        },
        "publicUrl": {
          "type": [
            "string",
            "null"
          ]
        },
        "customDomain": {
          "type": [
            "string",
            "null"
          ]
        },
        "embedHtml": {
          "type": [
            "string",
            "null"
          ]
        },
        "openRoleCount": {
          "type": "integer"
        },
        "appearance": {
          "type": "object",
          "description": "The board's own appearance settings. A null field is not set: Brand's value applies when source is brand, else the neutral default.",
          "properties": {
            "source": {
              "enum": [
                "brand",
                "manual"
              ],
              "description": "brand: follow the organization's published Brand presentation (logo, light and dark palettes, name) for every field the board does not set. manual: use only these settings and the neutral defaults."
            },
            "logoUrl": {
              "type": [
                "string",
                "null"
              ]
            },
            "primaryColor": {
              "type": [
                "string",
                "null"
              ],
              "description": "#rrggbb"
            },
            "coverImageUrl": {
              "type": [
                "string",
                "null"
              ]
            },
            "headline": {
              "type": [
                "string",
                "null"
              ]
            },
            "font": {
              "type": [
                "string",
                "null"
              ],
              "enum": [
                "system",
                "grotesk",
                "serif",
                "rounded",
                null
              ]
            },
            "themeMode": {
              "enum": [
                "light",
                "dark",
                "auto"
              ],
              "description": "light (the default) or dark renders only that palette; auto follows the visitor's device setting. A board saved before this field existed reads as light."
            },
            "headerLinks": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  }
                }
              }
            },
            "siteUrl": {
              "type": [
                "string",
                "null"
              ]
            },
            "footerLinks": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  }
                }
              }
            },
            "showPoweredBy": {
              "type": "boolean"
            },
            "logoReversedUrl": {
              "type": [
                "string",
                "null"
              ]
            },
            "heroVariant": {
              "enum": [
                "text",
                "image"
              ]
            },
            "subheadline": {
              "type": [
                "string",
                "null"
              ]
            },
            "cover": {
              "type": [
                "object",
                "null"
              ],
              "description": "The hero image: url, alt (required), focalX and focalY (0-100)."
            },
            "sections": {
              "type": "object",
              "description": "about { enabled, body }, whyJoin { enabled, items }, benefits { enabled, items }, gallery { enabled, images }, hiringProcess { enabled, steps }, always in that order."
            },
            "rolesList": {
              "type": "object",
              "properties": {
                "groupBy": {
                  "enum": [
                    "none",
                    "team",
                    "location"
                  ]
                },
                "filters": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "sort": {
                  "enum": [
                    "newest",
                    "title"
                  ]
                }
              }
            },
            "confirmationMessage": {
              "type": [
                "string",
                "null"
              ]
            },
            "seo": {
              "type": "object",
              "properties": {
                "metaTitle": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "metaDescription": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "shareImage": {
                  "type": [
                    "object",
                    "null"
                  ]
                }
              }
            }
          }
        },
        "draftAppearance": {
          "type": [
            "object",
            "null"
          ],
          "description": "The unpublished next appearance (job_board.draft.update), the same shape as appearance; null without unpublished changes."
        },
        "destination": {
          "type": "object",
          "description": "Where the board's pages are stored to live. hosted (default): Recruit serves them at publicUrl. web_site: a Topolo Web site renders the board natively at origin + basePath; while destinationActive, publicUrl, canonicals, JobPosting url and role links point there, Recruit's hosted board and role pages answer 301 to it, and the public apply endpoint accepts applications only from origin.",
          "properties": {
            "kind": {
              "enum": [
                "hosted",
                "web_site"
              ]
            },
            "siteId": {
              "type": "string"
            },
            "origin": {
              "type": "string"
            },
            "basePath": {
              "type": "string"
            },
            "boundAt": {
              "type": "string"
            },
            "boundBy": {
              "type": "string"
            }
          }
        },
        "destinationActive": {
          "type": "boolean",
          "description": "Whether the stored destination is honoured in this environment (RECRUIT_CAREERS_DESTINATIONS on). False for a hosted board; a stored web_site destination that is not active changes nothing until it is."
        },
        "destinationHistory": {
          "type": "array",
          "description": "The latest destination changes, newest first (up to 5): bind or unbind, who, and the site, origin and basePath before and after.",
          "items": {
            "type": "object",
            "properties": {
              "action": {
                "enum": [
                  "bind",
                  "unbind"
                ]
              },
              "actorUserId": {
                "type": "string"
              },
              "siteId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "origin": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "basePath": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "previousSiteId": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "previousOrigin": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "previousBasePath": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "createdAt": {
                "type": "string"
              }
            }
          }
        },
        "brand": {
          "type": "object",
          "description": "What Brand offers this board: status available (a published kit applies), none (nothing is published for the workspace), unavailable (Brand did not answer and nothing is cached) or unconfigured (Brand is not set up in this environment).",
          "properties": {
            "status": {
              "enum": [
                "available",
                "none",
                "unavailable",
                "unconfigured"
              ]
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "logoUrl": {
              "type": [
                "string",
                "null"
              ]
            },
            "primaryColor": {
              "type": [
                "string",
                "null"
              ]
            },
            "darkPrimaryColor": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        },
        "effectiveAppearance": {
          "type": "object",
          "description": "What the public pages render now: the board's settings over Brand (when followed) over the neutral defaults. brand says whether Brand was applied, not_followed (manual), none or unavailable.",
          "properties": {
            "name": {
              "type": "string"
            },
            "logoUrl": {
              "type": [
                "string",
                "null"
              ]
            },
            "headline": {
              "type": [
                "string",
                "null"
              ]
            },
            "primaryLight": {
              "type": "string"
            },
            "primaryDark": {
              "type": "string"
            },
            "font": {
              "enum": [
                "system",
                "grotesk",
                "serif",
                "rounded",
                "brand"
              ]
            },
            "themeMode": {
              "enum": [
                "light",
                "dark",
                "auto"
              ]
            },
            "logoReversedUrl": {
              "type": [
                "string",
                "null"
              ]
            },
            "markUrl": {
              "type": [
                "string",
                "null"
              ]
            },
            "markReversedUrl": {
              "type": [
                "string",
                "null"
              ]
            },
            "heroVariant": {
              "enum": [
                "text",
                "image"
              ]
            },
            "headerLinks": {
              "type": "array",
              "items": {
                "type": "object"
              }
            },
            "siteUrl": {
              "type": [
                "string",
                "null"
              ]
            },
            "footerLinks": {
              "type": "array",
              "items": {
                "type": "object"
              }
            },
            "showPoweredBy": {
              "type": "boolean"
            },
            "brand": {
              "enum": [
                "applied",
                "not_followed",
                "none",
                "unavailable"
              ]
            }
          }
        }
      }
    }
  }
}

Worked examples

Actual dispatcher and migration-created SQLite or Worker handlers; Auth/workspace identity, object storage, mail and Notify dependencies are simulated. These synthetic fixtures establish no live authorization or delivery. A synthetic fixture identifier grants no access. Validate and inspect the planned request before executing. Commands with --confirm or MCP confirm:true assume the intended effect or data export has already been authorized.

Successful request

Actual dispatcher and migration-created SQLite; verified Auth/workspace context, object storage, mail and Notify dependencies are simulated. No live delivery or credential authorization is established. This complete response is a local fixture, not a deployed or delivered result.

Input · payload.json:

{
  "slug": "acme",
  "title": "Acme"
}

CLI:

topolo actions validate app_topolo_recruit.job_board.update --data @payload.json --json
topolo actions plan app_topolo_recruit.job_board.update --resource-type workspace --resource-id "$RECRUIT_WORKSPACE_ID" --data @payload.json --json
topolo actions call app_topolo_recruit.job_board.update --resource-type workspace --resource-id "$RECRUIT_WORKSPACE_ID" --data @payload.json --confirm --json

MCP tool and arguments:

{
  "tool": "topolo_call_action",
  "arguments": {
    "actionId": "app_topolo_recruit.job_board.update",
    "input": {
      "slug": "acme",
      "title": "Acme"
    },
    "resourceType": "workspace",
    "resourceId": "<identifier returned by resource discovery>",
    "confirm": true
  }
}

Captured local response · HTTP 200:

{
  "board": {
    "id": "board_fb8f0809a1f54031ae60c37e5fec32d1",
    "slug": "acme",
    "title": "Acme",
    "intro": null,
    "published": false,
    "appearance": {
      "source": "brand",
      "logoUrl": null,
      "primaryColor": null,
      "coverImageUrl": null,
      "headline": null,
      "font": null,
      "footerLinks": [],
      "showPoweredBy": true
    },
    "destination": {
      "kind": "hosted"
    },
    "publicUrl": "https://recruit.test/jobs/acme",
    "customDomain": null,
    "embedHtml": "<iframe src=\"https://recruit.test/jobs/acme?embed=1\" title=\"Acme\" loading=\"lazy\" style=\"width:100%;min-height:640px;border:0\"></iframe>",
    "openRoleCount": 0,
    "brand": {
      "status": "unconfigured",
      "name": null,
      "logoUrl": null,
      "primaryColor": null,
      "darkPrimaryColor": null
    },
    "effectiveAppearance": {
      "name": "Acme",
      "logoUrl": null,
      "coverImageUrl": null,
      "headline": null,
      "primaryLight": "#0a76a9",
      "primaryDark": "#0a76a9",
      "font": "system",
      "footerLinks": [],
      "showPoweredBy": true,
      "brand": "none"
    },
    "destinationActive": false,
    "destinationHistory": []
  }
}

HTTP 200. Call job_board.get and confirm the slug, title, intro and appearance, and that effectiveAppearance shows what the pages should render.

Another supported request

Actual dispatcher and migration-created SQLite; verified Auth/workspace context, object storage, mail and Notify dependencies are simulated. No live delivery or credential authorization is established. This complete response is a local fixture, not a deployed or delivered result.

Input · payload.json:

{
  "slug": "acme",
  "title": "Acme"
}

CLI:

topolo actions validate app_topolo_recruit.job_board.update --data @payload.json --json
topolo actions plan app_topolo_recruit.job_board.update --resource-type workspace --resource-id "$RECRUIT_WORKSPACE_ID" --data @payload.json --json
topolo actions call app_topolo_recruit.job_board.update --resource-type workspace --resource-id "$RECRUIT_WORKSPACE_ID" --data @payload.json --confirm --json

MCP tool and arguments:

{
  "tool": "topolo_call_action",
  "arguments": {
    "actionId": "app_topolo_recruit.job_board.update",
    "input": {
      "slug": "acme",
      "title": "Acme"
    },
    "resourceType": "workspace",
    "resourceId": "<identifier returned by resource discovery>",
    "confirm": true
  }
}

Captured local response · HTTP 200:

{
  "board": {
    "id": "board_b5e1d5dc6b3647789e750ac4c779c495",
    "slug": "acme",
    "title": "Acme",
    "intro": null,
    "published": false,
    "appearance": {
      "source": "brand",
      "logoUrl": null,
      "primaryColor": null,
      "coverImageUrl": null,
      "headline": null,
      "font": null,
      "footerLinks": [],
      "showPoweredBy": true
    },
    "destination": {
      "kind": "hosted"
    },
    "publicUrl": "https://recruit.test/jobs/acme",
    "customDomain": null,
    "embedHtml": "<iframe src=\"https://recruit.test/jobs/acme?embed=1\" title=\"Acme\" loading=\"lazy\" style=\"width:100%;min-height:640px;border:0\"></iframe>",
    "openRoleCount": 0,
    "brand": {
      "status": "unconfigured",
      "name": null,
      "logoUrl": null,
      "primaryColor": null,
      "darkPrimaryColor": null
    },
    "effectiveAppearance": {
      "name": "Acme",
      "logoUrl": null,
      "coverImageUrl": null,
      "headline": null,
      "primaryLight": "#0a76a9",
      "primaryDark": "#0a76a9",
      "font": "system",
      "footerLinks": [],
      "showPoweredBy": true,
      "brand": "none"
    },
    "destinationActive": false,
    "destinationHistory": []
  }
}

HTTP 200. Call job_board.get and confirm the slug, title, intro and appearance, and that effectiveAppearance shows what the pages should render.

Refused request · invalid_input

Actual dispatcher and migration-created SQLite; verified Auth/workspace context, object storage, mail and Notify dependencies are simulated. No live delivery or credential authorization is established. This is a refused request; diagnose it before retrying.

Input · payload.json:

{
  "slug": "Not A Slug"
}

CLI:

topolo actions validate app_topolo_recruit.job_board.update --data @payload.json --json
topolo actions plan app_topolo_recruit.job_board.update --resource-type workspace --resource-id "$RECRUIT_WORKSPACE_ID" --data @payload.json --json

Captured local response · HTTP 422:

{
  "error": {
    "code": "invalid_input",
    "message": "The input does not match the action schema.",
    "messageKey": "recruit.api.errors.invalidInput",
    "locale": "en",
    "details": [
      {
        "origin": "string",
        "code": "invalid_format",
        "format": "regex",
        "pattern": "/^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/",
        "path": [
          "slug"
        ],
        "message": "Use lowercase letters, digits and single hyphens."
      }
    ]
  }
}

HTTP 422. Preserve the exact response code and details. Do not claim success.

Refused request · board_slug_taken

Actual dispatcher and migration-created SQLite; verified Auth/workspace context, object storage, mail and Notify dependencies are simulated. No live delivery or credential authorization is established. This is a refused request; diagnose it before retrying.

Input · payload.json:

{
  "slug": "acme"
}

CLI:

topolo actions validate app_topolo_recruit.job_board.update --data @payload.json --json
topolo actions plan app_topolo_recruit.job_board.update --resource-type workspace --resource-id "$RECRUIT_WORKSPACE_ID" --data @payload.json --json

Captured local response · HTTP 409:

{
  "error": {
    "code": "board_slug_taken",
    "message": "The job board address \"acme\" is already in use. Choose another.",
    "messageKey": "recruit.api.errors.boardSlugTaken",
    "messageValues": {
      "slug": "acme"
    },
    "locale": "en"
  }
}

HTTP 409. Preserve the exact response code and details. Do not claim success.

Verify the result

  • Call job_board.get and confirm the slug, title, intro and appearance, and that effectiveAppearance shows what the pages should render.

Errors and recovery

  • organization_context_required (HTTP 403): Select the organization authorized by the credential; do not send an organization id in the action input.
  • permission_denied (HTTP 403): Inspect the required Recruit permission and the credential’s granted workspace access. Use an already authorized credential/scope or request the missing access from its owner. Change credentials or grants only under separate explicit authorization; do not expand permissions automatically to clear this refusal.
  • hiring_team_role_required (HTTP 403): Ask a recruiter to give the caller a hiring-team role that allows this action (team.members.set); error.details lists the allowed roles.
  • workspace_required (HTTP 409): Create a Recruit workspace, or pass the workspace resource context.
  • invalid_input (HTTP 422): Correct the fields named in error.details using inputSchema, then retry.
  • board_slug_taken (HTTP 409): Choose another slug; this one belongs to another organization.
  • invalid_json (HTTP 400): Send a valid JSON object. The request is rejected before workspace resolution or record creation.
  • api_key_workspace_binding_required (HTTP 403): Use a key explicitly bound to the intended granted workspace. Inspect Auth api_keys.options before creating or changing a key, and obtain approval for any credential or access change; do not widen bindings automatically.
  • workspace_required (HTTP 400): Send the workspace the key is bound to with --resource-type workspace --resource-id <workspace id> (or the X-Topolo-Resource-Type and X-Topolo-Resource-Id headers).
  • workspace_forbidden (HTTP 403): Use a workspace the key is bound to; read the key's bindings in API key management.
  • workspace_access_insufficient (HTTP 403): The required workspace permission is workspace:read for reads or workspace:write for changes, including deletion. Inspect the authorized key options and resource access; request any missing access from its owner rather than granting it automatically.

Authentication or gateway failures may occur before the Recruit handler. Preserve the exact discovered service error, request id and credential context. The local fixtures do not establish live credential authorization.

Recovery boundary

Read job_board.get first, then send the previous slug, title, intro and appearance fields back.

Evidence

Actual dispatcher and migration-created SQLite or Worker handlers; Auth/workspace identity, object storage, mail and Notify dependencies are simulated. These synthetic fixtures establish no live authorization or delivery. Test capture provenance is retained with each fixture. API, database and integration outcomes require separate verification in the target environment.

Documentation history

2026-10-18

  • Added the current appearance input and board response, grouped the supported appearance controls, and clarified live versus draft updates and verification.
  • Updated this guide from source revision 63171ed7c08aa066adb44cc01e64011e465302e1; retained 4 source-captured request/response examples and their simulated-dependency limitations.
  • Clarified authorized API-key/resource recovery without automatic permission expansion. This documentation update does not establish deployment, live hiring operations or notification delivery.