| Location | Name | Required | Meaning |
|---|---|---|---|
| header | X-Admin-CSRF | Yes | Session-bound token from the staff page; not a developer credential. |
{
"type": "object",
"properties": {
"launchDate": {
"type": "string",
"format": "date-time",
"description": "ISO time with offset, within two years of the current time."
},
"appStoreUrl": {
"anyOf": [
{
"type": "string",
"maxLength": 300,
"description": "An https://apps.apple.com listing URL; empty or null clears it."
},
{
"type": "null"
}
]
},
"note": {
"anyOf": [
{
"type": "string",
"maxLength": 500,
"description": "Operator note."
},
{
"type": "null"
}
]
},
"expectedVersion": {
"type": "integer",
"minimum": 0,
"description": "Version loaded by the editor; conflict returns 409."
}
},
"required": [
"launchDate"
],
"additionalProperties": false
}{error:{code,message}} for CloudError failures, attaches Request-Id, and disables caching. Handle 401 by signing in; show role refusal for 403 forbidden, reauthenticate only for step_up_required, and collect a reason for 400 reason_required. A CSRF failure needs a current page/session token, not blind retries. Operation-specific 404/409/413/422/502/503 errors retain their status. Honor Retry-After on 429./** PUT /api/admin/config {launchDate, appStoreUrl?, note?, expectedVersion?} — owner only, fresh step-up, audited. */
export const PUT = adminRoute("config.write", async (request, ctx) => {
const input = await readBody(request, siteConfigInput());
return adminJson(await updateSiteConfig(ctx, input));
}, { stepUp: true, rateLimit: "config" });