{"openapi":"3.1.0","info":{"title":"Street Art Cities Edits API","description":"Make or suggest changes to Street Art Cities content, like artworks, artists and cities, and review suggestions made by others.","version":"1.0.0"},"servers":[{"url":"https://streetartcities.com","description":"Production"},{"url":"https://streetartdev.com","description":"Development"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"Edit":{"type":"object","properties":{"id":{"type":"string"},"entityId":{"description":"Missing for pending edits that create a new entity.","type":"string"},"entityType":{"type":"string","enum":["marker","artist","site","tag","calendar-event","community-note"],"description":"The kind of entity being changed. `site` is a city."},"actions":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"A map of changes. Keys are dot-paths into the entity (e.g. `title` or `attributes.access_note`). Values are either a literal to set, or an operation: `{ \"$add\": [...], \"$remove\": [...] }` for arrays, or `{ \"$unset\": true }` to remove a field. The special keys `$mergeInto` (another entity id), `$delete` (`true`, must be the only action) and `$renameTo` (a new name, tags only, not combined with `$mergeInto`) merge, delete or rename an existing entity.","example":{"attributes.access_note":"Only accessible whilst the shop is open.","tags":{"$add":["stencil"]}}},"status":{"type":"string","enum":["submitted","accepted","rejected","reverted"]},"createdBy":{"type":"string"},"createdAt":{"type":"string"},"reviewedBy":{"description":"`system` when the edit was applied automatically.","type":"string"},"reviewedAt":{"type":"string"},"revertedBy":{"type":"string"},"revertedAt":{"type":"string"},"editComment":{"type":"string"},"editAttachments":{"description":"Links to images attached to the edit comment.","type":"array","items":{"type":"string"}},"reviewComment":{"type":"string"},"assignedReviewers":{"type":"array","items":{"type":"string"},"description":"Groups and/or user ids that may review this edit."},"snapshotOld":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"snapshotNew":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"history":{"type":"array","items":{"type":"object","properties":{"status":{"type":"string","enum":["submitted","accepted","rejected","reverted"]},"at":{"type":"string"},"by":{"type":"string"},"comment":{"type":"string"}},"required":["status","at","by"]}},"site":{"type":"string"},"country":{"type":"string"},"attributes":{"description":"Extra details about the edit. `oauthClientId` is set when it was made through a third-party app.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"reviewUrl":{"type":"string","description":"Link to the page on the website where the edit can be reviewed, or its status seen."}},"required":["id","entityType","actions","status","createdBy","createdAt","assignedReviewers","history","reviewUrl"]},"EditView":{"type":"object","properties":{"edit":{"$ref":"#/components/schemas/Edit"},"revertible":{"type":"boolean"},"reviewable":{"type":"boolean","description":"Whether you can accept or reject this edit right now."},"changes":{"type":"array","items":{"$ref":"#/components/schemas/FieldChange"}}},"required":["edit","revertible","reviewable","changes"]},"FieldChange":{"type":"object","properties":{"path":{"type":"string"},"before":{},"after":{}},"required":["path","before","after"]}}},"security":[{"bearerAuth":[]}],"paths":{"/api/edits/history/{entityType}/{entityId}":{"get":{"operationId":"edits.history","summary":"Get entity history","description":"List the accepted and reverted edits for an entity, newest first. Doesn't require logging in.","tags":["Edits"],"security":[],"parameters":[{"name":"entityType","in":"path","required":true,"schema":{"type":"string","enum":["marker","artist","site","tag","calendar-event","community-note","city"]}},{"name":"entityId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The entity's history","content":{"application/json":{"schema":{"type":"object","properties":{"edits":{"type":"array","items":{"$ref":"#/components/schemas/Edit"}}},"required":["edits"]}}}}}}},"/api/edits":{"post":{"operationId":"edits.create","summary":"Create edit","description":"Make or suggest a change. If you're allowed to change the entity directly, the edit is applied straight away (`status: \"accepted\"`). Otherwise it's queued for review (`status: \"submitted\"`).\n\nEdits made with an OAuth token from a third-party app are always queued for review. Send people to the `reviewUrl` in the response: if they're allowed to review the edit, they can accept it there themselves.","tags":["Edits"],"x-scope-required":["edits:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"entityType":{"type":"string","enum":["marker","artist","site","tag","calendar-event","community-note","city"],"description":"The kind of entity being changed. `city` and `site` mean the same thing; edits are returned with `site`."},"entityId":{"description":"The entity to change. Leave out to create a new entity.","type":"string"},"actions":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"A map of changes. Keys are dot-paths into the entity (e.g. `title` or `attributes.access_note`). Values are either a literal to set, or an operation: `{ \"$add\": [...], \"$remove\": [...] }` for arrays, or `{ \"$unset\": true }` to remove a field. The special keys `$mergeInto` (another entity id), `$delete` (`true`, must be the only action) and `$renameTo` (a new name, tags only, not combined with `$mergeInto`) merge, delete or rename an existing entity.","example":{"attributes.access_note":"Only accessible whilst the shop is open.","tags":{"$add":["stencil"]}}},"editComment":{"type":"string"},"editAttachments":{"type":"array","items":{"type":"string"}}},"required":["entityType","actions"]}}}},"responses":{"201":{"description":"The created edit","content":{"application/json":{"schema":{"type":"object","properties":{"edit":{"$ref":"#/components/schemas/Edit"}},"required":["edit"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"No permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/edits/evaluate-permissions":{"post":{"operationId":"edits.evaluatePermissions","summary":"Check if an edit would be applied directly","description":"Find out whether a change would be applied straight away or queued for review, and whether you could accept it yourself, before submitting it. Changes made through third-party apps are never applied straight away.","tags":["Edits"],"x-scope-required":["edits:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"entityType":{"type":"string","enum":["marker","artist","site","tag","calendar-event","community-note","city"],"description":"The kind of entity being changed. `city` and `site` mean the same thing; edits are returned with `site`."},"entityId":{"type":"string"},"actions":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"A map of changes. Keys are dot-paths into the entity (e.g. `title` or `attributes.access_note`). Values are either a literal to set, or an operation: `{ \"$add\": [...], \"$remove\": [...] }` for arrays, or `{ \"$unset\": true }` to remove a field. The special keys `$mergeInto` (another entity id), `$delete` (`true`, must be the only action) and `$renameTo` (a new name, tags only, not combined with `$mergeInto`) merge, delete or rename an existing entity.","example":{"attributes.access_note":"Only accessible whilst the shop is open.","tags":{"$add":["stencil"]}}}},"required":["entityType"]}}}},"responses":{"200":{"description":"Whether the edit would be auto-accepted","content":{"application/json":{"schema":{"type":"object","properties":{"autoAccept":{"type":"boolean","description":"Whether the edit would be applied straight away. Always `false` for third-party apps."},"canAccept":{"type":"boolean","description":"Whether you could accept the edit yourself."},"reason":{"type":"string"}},"required":["autoAccept","canAccept"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"No permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/edits/views":{"post":{"operationId":"edits.views","summary":"Get pending edit previews","description":"Get change previews for up to 100 pending edits, in the order given. Edits you can't review, or that have already been reviewed, are left out.","tags":["Edits"],"x-scope-required":["edits:read"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"},"description":"Only the first 100 ids are used."}},"required":["ids"]}}}},"responses":{"200":{"description":"The edit previews","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/EditView"}}},"required":["items"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"No permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/edits/mine":{"get":{"operationId":"edits.mine","summary":"Get my edits","description":"List the edits made by the logged-in user, newest first. Pass `ids` to only get those edits, in the order given.","tags":["Edits"],"x-scope-required":["edits:read"],"parameters":[{"name":"ids","in":"query","required":false,"description":"Comma-separated edit ids, up to 100. Edits that aren't yours are left out.","schema":{"type":"string"}}],"responses":{"200":{"description":"Your edits","content":{"application/json":{"schema":{"type":"object","properties":{"edits":{"type":"array","items":{"$ref":"#/components/schemas/Edit"}}},"required":["edits"]}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/edits/{id}":{"get":{"operationId":"edits.get","summary":"Get edit","description":"Get a single edit with a before/after preview of its changes. Visible to the edit's author and anyone who can review it.","tags":["Edits"],"x-scope-required":["edits:read"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The edit","content":{"application/json":{"schema":{"type":"object","properties":{"edit":{"$ref":"#/components/schemas/Edit"},"revertible":{"type":"boolean"},"reviewable":{"type":"boolean","description":"Whether you can accept or reject this edit right now."},"changes":{"type":"array","items":{"$ref":"#/components/schemas/FieldChange"}}},"required":["edit","revertible","reviewable","changes"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"No permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/edits/{id}/accept":{"post":{"operationId":"edits.accept","summary":"Accept edit","description":"Accept a pending edit and apply it. For reviewers only.","tags":["Edits"],"x-scope-required":["edits:review"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reviewComment":{"type":"string"},"site":{"description":"Assign a new artwork to this city instead of the suggested one.","type":"string"}}}}}},"responses":{"200":{"description":"The accepted edit","content":{"application/json":{"schema":{"type":"object","properties":{"edit":{"$ref":"#/components/schemas/Edit"}},"required":["edit"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"No permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/edits/{id}/reject":{"post":{"operationId":"edits.reject","summary":"Reject edit","description":"Reject a pending edit, leaving the entity untouched. For reviewers only.","tags":["Edits"],"x-scope-required":["edits:review"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reviewComment":{"type":"string"}}}}}},"responses":{"200":{"description":"The rejected edit","content":{"application/json":{"schema":{"type":"object","properties":{"edit":{"$ref":"#/components/schemas/Edit"}},"required":["edit"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"No permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/edits/{id}/revert":{"post":{"operationId":"edits.revert","summary":"Revert edit","description":"Undo an accepted edit, restoring the fields it changed. Only works if the entity hasn't changed since. Merges and deletions can't be reverted.","tags":["Edits"],"x-scope-required":["edits:review"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reviewComment":{"type":"string"}}}}}},"responses":{"200":{"description":"The reverted edit","content":{"application/json":{"schema":{"type":"object","properties":{"edit":{"$ref":"#/components/schemas/Edit"}},"required":["edit"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"403":{"description":"No permission","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}}}}