{"openapi":"3.1.0","info":{"title":"Verified Handles API","version":"1.5.0","description":"Verified Handles as data: every public entry, its handles and images, its history, and search. It answers with the same data as the pages, built by the same code. Entry text is CC0, except a description taken from Wikipedia, which is CC BY-SA 4.0 (`description_license`): where you show it, show the article (`description_source.url`), the licence by name with a link ([CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)), and “adapted from” when `description_source.adapted` is true, and share alike. Images are licensed by their owners, and each says how (`image_rights`). Email addresses are never published as data.\n\n**Keys.** An entry answers to its VHID (11 digits, permanent), its short name, or an old name.\n\n**Paging.** Lists page by `limit` and `offset`; follow `next_offset` until it is null. Offsets stop at 10,000: past that they are clamped, silently.\n\n**Caching.** Responses say how long they may be kept (`Cache-Control`, and `x-cache` on each operation). An entry carries a strong ETag: send it back as `If-None-Match` and an unchanged entry answers 304.\n\n**Cross-origin.** `/api/v1` answers any origin (`Access-Control-Allow-Origin: *`, GET only, no credentials). The `.json` addresses beside the pages are same-origin.\n\n**Rate limit.** 120 requests a minute per IP address across the JSON reads; past it, a 429 with `Retry-After`.\n\n**Errors** are JSON: `{\"error\": \"<code>\"}`, with a field or two more where they help.","license":{"name":"CC0 1.0 (entry text, except a description taken from Wikipedia, which is CC BY-SA 4.0; images are licensed separately)","identifier":"CC0-1.0"}},"servers":[{"url":"https://verifiedhandles.org"}],"tags":[{"name":"Claiming an entry","description":"Proving that you run the person or organisation an entry is about."},{"name":"Drafts","description":"An edit to an entry you have started and not saved, kept with your account for 30 days after its last change."},{"name":"Proposing changes","description":"Asking for a change to an entry, or for a new entry, with an account: a reviewer looks at it before it goes live. Anyone with an account may propose."},{"name":"Your account","description":"Your own preferences."},{"name":"Entries","description":"An entry: who or what it is, its handles and its images."},{"name":"History","description":"Every revision of an entry: the history kept from the old wiki and each edit made since."},{"name":"Search","description":"Finding entries by name, words, a Wikidata QID or a VHID."},{"name":"About the API","description":"This document."},{"name":"Applying for an entry","description":"Applying for a new entry, with what shows it should have one."},{"name":"Entry manager","description":"One entry’s settings, its Wikidata item, merges, restores and bulk changes."},{"name":"Connecting an app","description":"The OAuth endpoints an MCP client uses to connect to a person’s account: where to send them to sign in (the discovery documents), its tokens, refreshing them, and disconnecting."},{"name":"Reporting an entry","description":"Telling the site that something on an entry is wrong, out of date or abusive."}],"paths":{"/api/entity/{vhid}/claims":{"post":{"operationId":"startClaim","tags":["Claiming an entry"],"summary":"Start a claim","description":"Starts proving that you run the person or organisation an entry is about. It answers a token: publish it on a page the entry lists (`url`), in a DNS TXT record of its website’s domain (`dns`); or, for `email`, no token is needed: your account’s verified email must be an address the entry lists, or admin@, administrator@, webmaster@, hostmaster@ or postmaster@ at a domain it lists. The site then checks it for itself. An entry closed to claims is refused. An entry someone else holds can be claimed too: its proof asks an administrator for a transfer.","parameters":[{"name":"vhid","in":"path","required":true,"description":"The entry’s VHID (11 digits).","schema":{"type":"string","pattern":"^\\d{11}$"},"example":"53500361835"}],"requestBody":{"description":"How you will prove it. At most 16 KiB.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"method":{"type":"string","enum":["url","dns","email"]},"target":{"description":"For `url`, the page; for `dns`, the domain. Not used for `email`.","type":"string"}},"required":["method"],"additionalProperties":false},"example":{"method":"url","target":"https://x.com/james"}}}},"responses":{"200":{"description":"The claim, with its token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimStarted"},"examples":{"url":{"summary":"By a page the entry lists","description":"POST /api/entity/53500361835/claims","value":{"ok":true,"claimId":"43","token":"vh-verify-7aa3f3873f6f706e0002d9de8eabb5cc","expires_at":"2026-10-12T09:00:00.000Z","method":"url","instructions":"Post this verification token on the profile/page listed on this entry, then verify: vh-verify-7aa3f3873f6f706e0002d9de8eabb5cc"}}}}}},"400":{"description":"The body is not JSON, the method is not one of the three, or `url`/`dns` came without a target. Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["bad_json","bad_method","target_required"],"description":"What is wrong with the body."}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/ApiKeyRefusal"}]},"examples":{"no-target":{"summary":"A page method with no page","description":"POST /api/entity/53500361835/claims","value":{"error":"target_required"}}}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"The entry takes no claims (`closed`). A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["closed"],"description":"The entry takes no claims."}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"No public entry has that VHID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"413":{"description":"The body is over 16 KiB.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["too_large"],"description":"The body is too large."}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":"claim","x-read-only-key":false,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/claims/{id}/verify":{"post":{"operationId":"verifyClaim","tags":["Claiming an entry"],"summary":"Check a claim’s proof","description":"Checks the claim’s proof by its method: the token on the page the entry lists (`url`, fetched once, through the same guard as every fetch the site makes), in a DNS TXT record (`dns`), or your account’s verified email being an address the entry lists or a role address (admin@, administrator@, webmaster@, hostmaster@, postmaster@) at a domain it lists (`email`). Found, the entry is claimed at once: it is yours, and the Wikidata sync stops changing it (unless it is locked to admins). Found on an entry someone else holds: 202 `{\"review\":\"transfer\"}`, and an administrator decides the transfer in the review queue; nothing changes until then. Any other address at a domain the entry lists is not proof, but evidence: 202 `{\"review\":\"domain\"}`, for an administrator to decide. Either way the claimant is emailed the decision, and checking again answers the same 202. Not found: 422, with what was seen, and the claim stays open to check again until it expires. Its submitter may check it, and so may a reviewer.","parameters":[{"name":"id","in":"path","required":true,"description":"The claim’s id (`claimId` when it was started).","schema":{"type":"string","pattern":"^\\d+$"},"example":"43"}],"responses":{"200":{"description":"The proof was found: the entry is claimed.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"vhid":{"type":"string"}},"required":["ok","vhid"],"additionalProperties":false}}}},"202":{"description":"Filed for an administrator: a passing proof on an entry someone else holds (`transfer`), or an ordinary address at a listed domain (`domain`).","content":{"application/json":{"schema":{"type":"object","properties":{"review":{"type":"string","enum":["transfer","domain"]}},"required":["review"],"additionalProperties":false}}}},"400":{"description":"The claim’s entry is gone (`no_entity`) or its proof is unusable (`bad_evidence`). Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["no_entity","bad_evidence"],"description":"The claim’s entry is gone, or its stored proof is unusable."}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"Not your claim and not a reviewer (`forbidden`), or the entry takes no claims now (`closed`). A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["closed"],"description":"The entry takes no claims now."}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"No such claim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"},"examples":{"missing":{"summary":"No such claim","description":"POST /api/claims/999999999/verify","value":{"error":"not_found"}}}}}},"409":{"description":"Already decided (`already_decided`), or decided or deleted while the proof ran (`conflict`).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["already_decided","conflict"],"description":"Why not."}},"required":["error"],"additionalProperties":false}}}},"410":{"description":"The token expired: start the claim again.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["expired"],"description":"Expired."}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"The token was not found: `detail` says what was seen. Put it in place and check again.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false},"detail":{"type":"string"}},"required":["ok","detail"],"additionalProperties":false}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":"claim","x-read-only-key":false,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/entity/{vhid}/draft":{"put":{"operationId":"putEntryDraft","tags":["Drafts"],"summary":"Keep a draft of an entry","description":"Keeps your unsaved edit to the entry, replacing any draft of it you had, and moves its expiry to 30 days from now. The description is cleaned with the same allowlist as a saved one. The body may be at most 256 KiB, and you may keep up to 100 drafts at once. A deleted entry takes no draft. Drafts are not in the audit log.","parameters":[{"name":"vhid","in":"path","required":true,"description":"The entry’s VHID (11 digits).","schema":{"type":"string","pattern":"^\\d{11}$"}}],"requestBody":{"description":"The draft, and what it was edited against.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"draft":{"$ref":"#/components/schemas/EntryDraft"},"base_revision_id":{"description":"The revision the edit started from (the editor’s base revision), or null.","anyOf":[{"type":"string","pattern":"^\\d+$"},{"type":"null"}]}},"required":["draft"],"additionalProperties":false},"example":{"draft":{"fields":{"title":"James Haworth","subtitle":"Founder of Verified Handles"}},"base_revision_id":null}}}},"responses":{"200":{"description":"Kept.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"vhid":{"type":"string"},"base_revision_id":{"anyOf":[{"type":"string","pattern":"^\\d+$"},{"type":"null"}],"description":"The revision the draft was edited against, or null."},"saved_at":{"type":"string","format":"date-time","description":"Its last change."},"expires_at":{"type":"string","format":"date-time","description":"When it is deleted unless changed again: 30 days after `saved_at`."}},"required":["ok","vhid","base_revision_id","saved_at","expires_at"],"additionalProperties":false},"examples":{"keep":{"summary":"A changed subtitle","description":"PUT /api/entity/53500361835/draft","value":{"ok":true,"vhid":"53500361835","base_revision_id":null,"saved_at":"2026-10-06T12:00:00.000Z","expires_at":"2026-11-05T12:00:00.000Z"}}}}}},"400":{"description":"`bad_json`: the body is not JSON. `bad_payload`: the body or the draft is not the editor’s shape; `field` names the part. `bad_base_revision`: `base_revision_id` is not one of this entry’s revisions. Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"anyOf":[{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["bad_json","bad_base_revision"],"description":"What was wrong."}},"required":["error"],"additionalProperties":false},{"type":"object","properties":{"error":{"type":"string","const":"bad_payload"},"field":{"type":"string","description":"The part that was wrong, e.g. `handles` or `fields.title`."}},"required":["error","field"],"additionalProperties":false}]},{"$ref":"#/components/schemas/ApiKeyRefusal"}]},"examples":{"bad":{"summary":"A key the editor does not write","description":"PUT /api/entity/53500361835/draft","value":{"error":"bad_payload","field":"colour"}}}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"No entry has that VHID (`not_found`), or it is deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["not_found"],"description":"No such entry."}},"required":["error"],"additionalProperties":false}}}},"409":{"description":"You already keep 100 drafts: save or discard one first.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["too_many_drafts"],"description":"Too many drafts."}},"required":["error"],"additionalProperties":false}}}},"413":{"description":"The body is over 256 KiB.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["too_large"],"description":"The body is too large."}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false},"get":{"operationId":"getEntryDraft","tags":["Drafts"],"summary":"Read your draft of an entry","description":"Your unsaved edit to the entry, as you last kept it, in the editor’s own shape.","parameters":[{"name":"vhid","in":"path","required":true,"description":"The entry’s VHID (11 digits).","schema":{"type":"string","pattern":"^\\d{11}$"}}],"responses":{"200":{"description":"The draft.","content":{"application/json":{"schema":{"type":"object","properties":{"vhid":{"type":"string"},"base_revision_id":{"anyOf":[{"type":"string","pattern":"^\\d+$"},{"type":"null"}],"description":"The revision the draft was edited against, or null."},"saved_at":{"type":"string","format":"date-time","description":"Its last change."},"expires_at":{"type":"string","format":"date-time","description":"When it is deleted unless changed again: 30 days after `saved_at`."},"draft":{"$ref":"#/components/schemas/EntryDraft"}},"required":["vhid","base_revision_id","saved_at","expires_at","draft"],"additionalProperties":false},"examples":{"mine":{"summary":"The draft kept above","description":"GET /api/entity/53500361835/draft","value":{"vhid":"53500361835","base_revision_id":null,"saved_at":"2026-10-06T12:00:00.000Z","expires_at":"2026-11-05T12:00:00.000Z","draft":{"fields":{"title":"James Haworth","subtitle":"Founder of Verified Handles"},"vhid":"53500361835","savedAt":1791288000000}}}}}}},"400":{"description":"Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"No entry has that VHID (`not_found`), or you keep no draft of it, or it expired (`no_draft`).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["not_found","no_draft"],"description":"What was not found."}},"required":["error"],"additionalProperties":false}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false},"delete":{"operationId":"deleteEntryDraft","tags":["Drafts"],"summary":"Discard your draft of an entry","description":"Deletes your draft of the entry, a deleted entry’s too. `deleted` is false when there was none to delete.","parameters":[{"name":"vhid","in":"path","required":true,"description":"The entry’s VHID (11 digits).","schema":{"type":"string","pattern":"^\\d{11}$"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"deleted":{"type":"boolean"}},"required":["ok","deleted"],"additionalProperties":false},"examples":{"discard":{"summary":"The draft kept above","description":"DELETE /api/entity/53500361835/draft","value":{"ok":true,"deleted":true}}}}}},"400":{"description":"Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"No entry has that VHID (`not_found`).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["not_found"],"description":"No such entry."}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/entity/drafts":{"get":{"operationId":"listEntryDrafts","tags":["Drafts"],"summary":"List your drafts","description":"Every draft you keep, the most recently changed first, without their contents.","parameters":[],"responses":{"200":{"description":"Your drafts.","content":{"application/json":{"schema":{"type":"object","properties":{"drafts":{"type":"array","items":{"$ref":"#/components/schemas/DraftListItem"}}},"required":["drafts"],"additionalProperties":false},"examples":{"mine":{"summary":"The one draft kept above","description":"GET /api/entity/drafts","value":{"drafts":[{"vhid":"53500361835","short":"james","title":"James Haworth","base_revision_id":null,"saved_at":"2026-10-06T12:00:00.000Z","expires_at":"2026-11-05T12:00:00.000Z","available":true}]}}}}}},"400":{"description":"Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/editor/{vhid}":{"get":{"operationId":"getEditorContext","tags":["Proposing changes"],"summary":"Read an entry for editing","description":"What the editor opens with: the fields a change may send, as they are now, the current revision to send as `base_revision_id`, each handle with who manages it, the platforms, and what the caller may do (`capabilities`). Signed in, it carries your own unsaved draft of the entry too (`draft`). Signed out it answers too, with every capability false. Read it before proposing a change. An email address is shown only to a caller who may edit the entry live; anyone else gets it masked, and sends it back as it came to keep it.","parameters":[{"name":"vhid","in":"path","required":true,"description":"The entry’s VHID (11 digits). These routes take no short name.","schema":{"type":"string","pattern":"^\\d{11}$"},"example":"53500361835"}],"responses":{"200":{"description":"The entry, for editing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditorContext"}}}},"400":{"description":"Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"No public entry has that VHID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{},{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"optional","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/entity/{vhid}/revisions/{ref}":{"get":{"operationId":"getRevisionState","tags":["Proposing changes"],"summary":"Read an entry as it was","description":"The whole entry as it was at one revision made on Verified Handles, for proposing to put it back. History kept from the old wiki is wikitext, not fields: `not_restorable`. A hidden revision is `not_found`. An email address is masked, as in the editor’s context, for an account that may not edit the entry live.","parameters":[{"name":"vhid","in":"path","required":true,"description":"The entry’s VHID (11 digits). These routes take no short name.","schema":{"type":"string","pattern":"^\\d{11}$"},"example":"53500361835"},{"name":"ref","in":"path","required":true,"description":"The revision, `r<n>`, as the entry’s history names it.","schema":{"type":"string","pattern":"^r\\d+$"},"example":"r1"}],"responses":{"200":{"description":"The entry as it was.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevisionState"},"examples":{"james":{"summary":"The first revision","description":"GET /api/entity/53500361835/revisions/r1","value":{"vhid":"53500361835","revision":{"ref_id":"r1","ts":"2026-09-19T12:00:00.000Z","summary":null,"hidden":false},"fields":{"title":"James Haworth","subtitle":null,"description":null,"short":"james","image_id":"3","gallery":[],"image_links":{},"custom_handle_order":false},"handles":[{"platform_id":"twitter","value":"james","display":null},{"platform_id":"instagram","value":"jameshaworth","display":null},{"platform_id":"linkedin","value":"james-haworth","display":null},{"platform_id":"email","value":null,"display":null,"masked":true,"ref":"4"}],"unavailable_images":[]}}}}}},"400":{"description":"The revision is not one made on Verified Handles (`not_restorable`). Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["not_restorable"],"description":"The revision is not one made on Verified Handles."}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/ApiKeyRefusal"}]},"examples":{"archive":{"summary":"Not a revision made here","description":"GET /api/entity/53500361835/revisions/a1","value":{"error":"not_restorable"}}}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"The account may not propose to this entry. A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["forbidden"],"description":"The account may not propose to this entry."}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"No public entry has that VHID, or no visible revision has that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":"propose","x-read-only-key":true,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/entity/search":{"get":{"operationId":"searchEntriesToLink","tags":["Proposing changes"],"summary":"Find an entry to link to","description":"The editor’s link search: up to 8 entries by name, words or QID, for a link in a description. Unlike the public search it is never cached, so an entry made a moment ago is found. A word the site does not search for answers an empty list.","parameters":[{"name":"q","in":"query","required":true,"description":"What to look for. Trimmed and cut to 200 characters; empty answers an empty list.","schema":{"type":"string","maxLength":200},"example":"james"}],"responses":{"200":{"description":"The best matches first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkSearch"},"examples":{"james":{"summary":"By name","description":"GET /api/entity/search?q=james","value":{"results":[{"vhid":"53500361835","title":"James Haworth","subtitle":null,"short":"james"}]}}}}}},"400":{"description":"Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]},"examples":{"signed-out":{"summary":"With no key","description":"GET /api/entity/search?q=james","value":{"error":"auth_required"}}}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/entity/{vhid}/proposals":{"post":{"operationId":"proposeChange","tags":["Proposing changes"],"summary":"Propose a change to an entry","description":"Queues a change for a reviewer, who approves it into the entry or turns it down. Send only the fields that change, with the `baseRevisionId` you read as `base_revision_id`. Proposing the same change again while it waits answers the same proposal. `qid_link` proposes linking the entry to a Wikidata item (or, with `qid: null`, unlinking it). Handle ops (`add_handles` / `remove_handles`) are stored as ops and applied to the entry’s list as it is when the proposal is approved, so two proposals adding different handles both apply; a proposal of nothing but ops is never held to its base revision, and one whose every op would change nothing now is refused (`no_changes`). Takes an optional `Idempotency-Key`, so a request whose answer was lost can be sent again safely: the retry gets the first answer back.","parameters":[{"name":"vhid","in":"path","required":true,"description":"The entry’s VHID (11 digits). These routes take no short name.","schema":{"type":"string","pattern":"^\\d{11}$"},"example":"53500361835"},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key you choose for this one request (1–255 printable ASCII characters, e.g. a UUID). Send the same request with the same key again, within 24 hours, and the first answer comes back (`Idempotency-Replayed: true`) with nothing written again. The key is your account’s and this route’s alone. \"The same request\" is the same path and the same JSON (key order and spacing aside; `base_revision_id` aside, so a retry made after re-reading the entry still matches). Only a 2xx answer is kept: after any other, nothing was written and the key is free to retry.","schema":{"type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"example":"0f8b3c5e-1d2a-4e7b-9c61-5a4d3e2f1b0a"}],"requestBody":{"description":"The change, under `payload` (or the change itself as the body).","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"$ref":"#/components/schemas/EditPayload"},"base_revision_id":{"description":"The revision the change was made against (`baseRevisionId`).","type":"string"},"prepared_by":{"description":"Sent by the site’s own pages when an AI agent in the person’s browser prepared the change (WebMCP). Recorded on the audit row, and on a proposal shown to its reviewer; it changes nothing else. Any other value is ignored.","type":"string","const":"webmcp"},"qid_link":{"type":"object","properties":{"qid":{"description":"The item, `Q<n>`; null or empty unlinks.","type":["string","null"]},"adopt":{"description":"Fields to take from the item when the link is approved.","type":"array","items":{"type":"string"}}},"required":["qid"],"additionalProperties":false}},"required":["payload"],"additionalProperties":false},"example":{"payload":{"subtitle":"Founder of Verified Handles"},"base_revision_id":"1"}}}},"responses":{"200":{"description":"Queued.","headers":{"Idempotency-Replayed":{"description":"`true` when this is the stored answer to an earlier request with the same Idempotency-Key: nothing was written now.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalQueued"},"examples":{"subtitle":{"summary":"Propose a subtitle","description":"POST /api/entity/53500361835/proposals","value":{"ok":true,"submissionId":"41","status":"pending"}}}}}},"400":{"description":"The change is malformed, would change nothing, or names a platform the catalog does not have or has disabled (`invalid_platform`). Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one. Or the Idempotency-Key is not 1–255 printable ASCII characters (`bad_idempotency_key`).","content":{"application/json":{"schema":{"anyOf":[{"anyOf":[{"type":"object","properties":{"error":{"type":"string","description":"`bad_json`: the body is not JSON. `bad_payload`: a field has the wrong type. `<field>_too_long`: a field is over 64 KiB. `blocked_content`: the text has a word the site does not take. Others name what is wrong: `invalid_image`, `invalid_image_link`, `no_changes`, a website that can never link. `invalid_platform`: a handle names a platform the catalog does not have, or a new handle is on a disabled one (a handle the entry already has on it is kept); `field`, `platform_id`, `reason` and `message` say which. `handles_with_ops`: `handles` beside `add_handles` / `remove_handles`. `masked_handle_needs_ref`: an email the caller may not see named by an address. A `no_changes` of handle ops says what each came to (`handle_ops`)."},"field":{"description":"With `invalid_platform`: the handle, as `handles[<i>].platform_id` (or `add_handles[<i>]…`).","type":"string"},"platform_id":{"description":"With `invalid_platform`: the platform id refused.","type":"string"},"reason":{"description":"With `invalid_platform`: the catalog has no such platform (`unknown`), or it is disabled (`disabled`).","type":"string","enum":["unknown","disabled"]},"message":{"description":"With `invalid_platform`: the refusal in words, naming the id and where the platform ids are listed (MCP `platform_catalog`).","type":"string"},"handle_ops":{"description":"With `no_changes` from handle ops: each one a no-op.","$ref":"#/components/schemas/HandleOpsReport"}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/ApiKeyRefusal"}]},{"type":"object","properties":{"error":{"type":"string","enum":["bad_idempotency_key","idempotency_key_reused","idempotency_key_in_progress"],"description":"What was wrong with the Idempotency-Key."},"message":{"type":"string","description":"The refusal in words."}},"required":["error","message"],"additionalProperties":false}]},"examples":{"bad":{"summary":"A field of the wrong type","description":"POST /api/entity/53500361835/proposals","value":{"error":"bad_payload"}},"ops-and-list":{"summary":"The whole handle list beside handle ops","description":"POST /api/entity/53500361835/proposals","value":{"error":"handles_with_ops"}},"platform":{"summary":"A handle on a platform the catalog does not have","description":"POST /api/entity/53500361835/proposals","value":{"error":"invalid_platform","field":"handles[0].platform_id","platform_id":"twiter","reason":"unknown","message":"handles[0].platform_id: \"twiter\" is not a platform in the catalog. Use a platform id from the platform catalog (MCP: platform_catalog, or get_entry with include_platforms)."}}}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"The entry takes no proposals from this account (`forbidden`), or a link on an image needs `link_images`. A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["image_link_forbidden"],"description":"A link on an image needs the `link_images` capability."}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"No public entry has that VHID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"409":{"description":"The Wikipedia page named in `wikipedia_credit` cannot be credited: missing, not an article or a draft, a section, no lead, a draft of an item that has an article, or the description is not the page’s text (`credit_text_differs`). Or the first request with this Idempotency-Key is still running (`idempotency_key_in_progress`): try again after `Retry-After` seconds.","headers":{"Retry-After":{"description":"With `idempotency_key_in_progress`: seconds to wait.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","description":"Why the named page cannot be credited (see `wikipedia_credit`)."},"page":{"description":"The page Wikipedia resolved the name to.","type":"string"},"share":{"description":"With `credit_text_differs`: the share (0–1) of the description’s phrases that are the page’s.","type":"number"},"article":{"description":"With `draft_has_article`: the item’s English article.","type":"string"}},"required":["error"],"additionalProperties":false},{"type":"object","properties":{"error":{"type":"string","enum":["bad_idempotency_key","idempotency_key_reused","idempotency_key_in_progress"],"description":"What was wrong with the Idempotency-Key."},"message":{"type":"string","description":"The refusal in words."}},"required":["error","message"],"additionalProperties":false}]}}}},"413":{"description":"The body is over 512 KiB.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["too_large"],"description":"The body is too large."}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"This Idempotency-Key was used for a different request in the last 24 hours (`idempotency_key_reused`): nothing was written.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["bad_idempotency_key","idempotency_key_reused","idempotency_key_in_progress"],"description":"What was wrong with the Idempotency-Key."},"message":{"type":"string","description":"The refusal in words."}},"required":["error","message"],"additionalProperties":false}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"502":{"description":"Wikipedia (or Wikidata) did not answer the read of a named page: try again.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["wikipedia_unreachable"],"description":"Upstream."}},"required":["error"],"additionalProperties":false}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":"propose","x-read-only-key":false,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/entity":{"post":{"operationId":"proposeNewEntry","tags":["Proposing changes"],"summary":"Propose a new entry","description":"Queues a new entry for a reviewer. Give it a title and, if it has one, its Wikidata item (`qid`); an entry already on that item is named instead (409), and an item Wikidata has merged into another is refused (409 `qid_merged`, `wikidata_item` names the one it kept: propose on that). A new entry has no images until it exists. While new entries are paused, it answers 503 `not_accepting`. (An account that may create entries itself is not paused.) A short a site redirect holds is refused (409 `short_is_redirect`). With `?check=1` it runs every check and queues nothing: 200 `{ ok: true, check: true }`, or the refusal the real call would answer (an `Idempotency-Key` is not recorded for it). Takes an optional `Idempotency-Key`, so a request whose answer was lost can be sent again safely: the retry gets the first answer back.","parameters":[{"name":"check","in":"query","required":false,"description":"Send `1` to check the proposal without queueing it, or leave it out; any other value is `bad_check` (400).","schema":{"type":"string","const":"1"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"A key you choose for this one request (1–255 printable ASCII characters, e.g. a UUID). Send the same request with the same key again, within 24 hours, and the first answer comes back (`Idempotency-Replayed: true`) with nothing written again. The key is your account’s and this route’s alone. \"The same request\" is the same path and the same JSON (key order and spacing aside; `base_revision_id` aside, so a retry made after re-reading the entry still matches). Only a 2xx answer is kept: after any other, nothing was written and the key is free to retry.","schema":{"type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"example":"0f8b3c5e-1d2a-4e7b-9c61-5a4d3e2f1b0a"}],"requestBody":{"description":"The new entry, under `payload`, and its Wikidata item.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"payload":{"$ref":"#/components/schemas/EditPayload"},"qid":{"description":"Its Wikidata item, `Q<n>`. One that is sent must be a real QID.","type":["string","null"]}},"required":["payload"],"additionalProperties":false},"example":{"payload":{"title":"Ada Lovelace","subtitle":"Mathematician"},"qid":"Q7259"}}}},"responses":{"200":{"description":"Queued; or with `?check=1`, it would be (nothing is queued).","headers":{"Idempotency-Replayed":{"description":"`true` when this is the stored answer to an earlier request with the same Idempotency-Key: nothing was written now.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ProposalQueued"},{"type":"object","properties":{"ok":{"type":"boolean","const":true},"check":{"type":"boolean","const":true}},"required":["ok","check"],"additionalProperties":false}]},"examples":{"ada":{"summary":"Propose an entry for a Wikidata item","description":"POST /api/entity","value":{"ok":true,"submissionId":"42","status":"pending"}},"check":{"summary":"Check a proposal without queueing it","description":"POST /api/entity?check=1","value":{"ok":true,"check":true}}}}}},"400":{"description":"The proposal is malformed: no title (`title_required`), a bad QID (`bad_qid`), an image (`image_on_new_entry`), a Wikipedia credit (`credit_on_new_entry`: name it once the entry exists), a handle on a platform the catalog does not have or has disabled (`invalid_platform`, naming it), a field of the wrong type, or a `check` that is not `1` (`bad_check`). Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one. Or the Idempotency-Key is not 1–255 printable ASCII characters (`bad_idempotency_key`).","content":{"application/json":{"schema":{"anyOf":[{"anyOf":[{"type":"object","properties":{"error":{"type":"string","description":"`bad_json`: the body is not JSON. `bad_payload`: a field has the wrong type. `<field>_too_long`: a field is over 64 KiB. `blocked_content`: the text has a word the site does not take. Others name what is wrong: `invalid_image`, `invalid_image_link`, `no_changes`, a website that can never link. `invalid_platform`: a handle names a platform the catalog does not have, or a new handle is on a disabled one (a handle the entry already has on it is kept); `field`, `platform_id`, `reason` and `message` say which. `handles_with_ops`: `handles` beside `add_handles` / `remove_handles`. `masked_handle_needs_ref`: an email the caller may not see named by an address. A `no_changes` of handle ops says what each came to (`handle_ops`)."},"field":{"description":"With `invalid_platform`: the handle, as `handles[<i>].platform_id` (or `add_handles[<i>]…`).","type":"string"},"platform_id":{"description":"With `invalid_platform`: the platform id refused.","type":"string"},"reason":{"description":"With `invalid_platform`: the catalog has no such platform (`unknown`), or it is disabled (`disabled`).","type":"string","enum":["unknown","disabled"]},"message":{"description":"With `invalid_platform`: the refusal in words, naming the id and where the platform ids are listed (MCP `platform_catalog`).","type":"string"},"handle_ops":{"description":"With `no_changes` from handle ops: each one a no-op.","$ref":"#/components/schemas/HandleOpsReport"}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/ApiKeyRefusal"}]},{"type":"object","properties":{"error":{"type":"string","enum":["bad_idempotency_key","idempotency_key_reused","idempotency_key_in_progress"],"description":"What was wrong with the Idempotency-Key."},"message":{"type":"string","description":"The refusal in words."}},"required":["error","message"],"additionalProperties":false}]},"examples":{"untitled":{"summary":"No title","description":"POST /api/entity","value":{"error":"title_required"}}}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"The account may not propose (`forbidden`). A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"409":{"description":"An entry is already on that Wikidata item (perhaps a deleted one): it is named (`already_exists`). Or Wikidata has merged the item into another (`qid_merged`): `wikidata_item` names the item it kept, and nothing is queued. Or the short is the address of a site redirect (`short_is_redirect`): choose another, or an administrator deletes the redirect first. Or the first request with this Idempotency-Key is still running (`idempotency_key_in_progress`): try again after `Retry-After` seconds.","headers":{"Retry-After":{"description":"With `idempotency_key_in_progress`: seconds to wait.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"anyOf":[{"anyOf":[{"type":"object","properties":{"error":{"type":"string","const":"already_exists"},"entry":{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string"}},"required":["vhid","short","title"],"additionalProperties":false}},"required":["error","entry"],"additionalProperties":false},{"type":"object","properties":{"error":{"type":"string","const":"qid_merged"},"qid":{"type":"string"},"wikidata_item":{"type":"string"},"message":{"type":"string"},"retry":{"type":"boolean","const":false}},"required":["error","qid","wikidata_item","message","retry"],"additionalProperties":false},{"type":"object","properties":{"error":{"type":"string","const":"short_is_redirect"}},"required":["error"],"additionalProperties":false}]},{"type":"object","properties":{"error":{"type":"string","enum":["bad_idempotency_key","idempotency_key_reused","idempotency_key_in_progress"],"description":"What was wrong with the Idempotency-Key."},"message":{"type":"string","description":"The refusal in words."}},"required":["error","message"],"additionalProperties":false}]}}}},"413":{"description":"The body is over 512 KiB.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["too_large"],"description":"The body is too large."}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"This Idempotency-Key was used for a different request in the last 24 hours (`idempotency_key_reused`): nothing was written.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["bad_idempotency_key","idempotency_key_reused","idempotency_key_in_progress"],"description":"What was wrong with the Idempotency-Key."},"message":{"type":"string","description":"The refusal in words."}},"required":["error","message"],"additionalProperties":false}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"New entries are paused (`not_accepting`, JSON), or the site is down for maintenance (an HTML page). Try again later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotAccepting"}}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":"propose","x-read-only-key":false,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/entity/batch":{"post":{"operationId":"editEntriesBatch","tags":["Proposing changes"],"summary":"Propose or make many changes at once","description":"Up to 25 edits in one request, each to a different entry: all proposals (`mode: \"propose\"`, as `POST /api/entity/{vhid}/proposals`) or all live edits (`mode: \"live\"`, as the editor’s Save makes them). Each edit is judged and made exactly as the single route makes it: whether this account may (entry by entry: a live edit needs `edit_live`, or `edit_locked` on an entry locked to admins), the conflict check against `base_revision_id`, one revision and one audit row per entry. A batch grants nothing a single call would refuse. One thing it refuses that a single call takes: naming a Wikipedia page for the credit (`credit_in_batch`), which the server reads from Wikipedia; send that one alone.\n\n**One at a time** (the default): each edit in its own transaction, in order. One edit refused leaves the others made; the answer is 200 with each edit’s outcome, in request order, with the code and status the single route would have answered.\n\n**`atomic: true`**: all or nothing. Every entry is locked first, in VHID order, and any refused edit undoes the whole batch: 422 `batch_failed`, naming every refused edit and why; the rest are `not_applied`, and nothing is written.\n\n**Replaying** a batch whose answer was lost is safe: an edit already made answers `conflict`, with `revisions_since_base: 1` and `all_since_base_by_you: true`; a proposal already filed answers its own `submission_id`. Every audit row of a batch carries its `batch_id`.\n\n**Rate limit.** Every edit counts as one change against the IP address, account and key, so 25 edits cost what they would cost sent one by one; the request is refused (429) before any edit runs when they do not fit. Takes an optional `Idempotency-Key`, so a request whose answer was lost can be sent again safely: the retry gets the first answer back.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"A key you choose for this one request (1–255 printable ASCII characters, e.g. a UUID). Send the same request with the same key again, within 24 hours, and the first answer comes back (`Idempotency-Replayed: true`) with nothing written again. The key is your account’s and this route’s alone. \"The same request\" is the same path and the same JSON (key order and spacing aside; `base_revision_id` aside, so a retry made after re-reading the entry still matches). Only a 2xx answer is kept: after any other, nothing was written and the key is free to retry.","schema":{"type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"example":"0f8b3c5e-1d2a-4e7b-9c61-5a4d3e2f1b0a"}],"requestBody":{"description":"The batch.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["live","propose"],"description":"Live edits or proposals: one kind per batch."},"atomic":{"description":"All or nothing. Default false: each edit on its own.","type":"boolean"},"summary":{"description":"Each live edit’s why, unless it gives its own. A proposal takes none.","type":"string","maxLength":500},"edits":{"minItems":1,"maxItems":25,"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"description":"HTML, sanitised on the way in by the same allowlist as every description. At most 64 KiB.","type":["string","null"]},"description_source":{"description":"Only `null`, which takes the \"from Wikipedia\" credit off. A credit is never sent: name its page with `wikipedia_credit`.","type":"null"},"wikipedia_credit":{"description":"Credit the description “from Wikipedia”. The server reads the page itself (redirects followed, so a draft since moved is its article), and refuses it unless at least half of the description’s three-word phrases are the page’s lead (`409 credit_text_differs`, with `page` and `share`). Not with `description_source: null`. Errors: `400 bad_wikipedia_title` / `not_english_wikipedia`; `409 wikipedia_page_missing`, `wikipedia_namespace` (not an article or a draft), `wikipedia_section`, `wikipedia_no_lead`, `no_description`, `draft_has_article` (the entry’s item already has an English article, named in `article`: credit that); `502 wikipedia_unreachable`.","type":"object","properties":{"page":{"type":"string","description":"An English Wikipedia article or draft, by title (`Draft:Dominic McLaughlin`) or address (`https://en.wikipedia.org/wiki/…`)."},"let_wikidata_manage":{"description":"For a draft only: `false` keeps the description site-authored. By default a draft’s credit hands the description to the Wikidata sync, which leaves it alone until the draft is an article linked from the entry’s Wikidata item.","type":"boolean"}},"required":["page"],"additionalProperties":false},"short":{"description":"The short name (`/<short>`); `null` removes it.","type":["string","null"]},"image_id":{"description":"The lead image, by media id; `null` removes it.","type":["string","null"]},"gallery":{"description":"The whole gallery after the lead, in order, by media id: it replaces the one there.","type":"array","items":{"type":"string"}},"image_links":{"description":"The whole map of links on the entry’s images, media id → absolute http(s) address. Needs the `link_images` capability.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"custom_handle_order":{"description":"Keep `handles` in the order given, rather than the site’s order.","type":"boolean"},"handles":{"description":"The whole list of handles, in order: it replaces the one there. Not with `add_handles` / `remove_handles`.","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string"},"value":{"type":"string"},"display":{"description":"A label to show instead of the value.","type":["string","null"]}},"required":["platform_id","value"],"additionalProperties":false}},"add_handles":{"description":"Handles to put on the entry, at the end of its list as it is when the change is applied (a proposal: when it is approved). One the entry has already (the same value, in any case the platform folds) changes nothing. No base revision is needed for it. Not with `handles` (400 `handles_with_ops`).","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string"},"value":{"type":"string","description":"Not blank. A website is completed to an address, as in `handles`."},"display":{"description":"A label to show instead of the value.","type":["string","null"]}},"required":["platform_id","value"],"additionalProperties":false}},"remove_handles":{"description":"Handles to take off, by value — or, for an email the caller may not see, by `ref` (an address is refused: `masked_handle_needs_ref`). One the entry does not have changes nothing. A value the Wikidata sync supplied is held back from it, as when a list leaves it out. Not with `handles`.","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"description":"The handle, as stored or in any case the platform folds.","type":"string"},"ref":{"description":"Instead of `value`, for an email the caller may not see: the `ref` `GET /api/editor/{vhid}` gives it.","type":"string"}},"required":["platform_id"],"additionalProperties":false}},"relinquish":{"description":"Fields to hand back to the Wikidata sync.","type":"array","items":{"type":"string"}},"takeover":{"description":"Fields to take from the Wikidata sync, keeping their value.","type":"array","items":{"type":"string"}},"handle_takeover":{"description":"Handles to take from the Wikidata sync.","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"type":"string","description":"The handle as stored."}},"required":["platform_id","value"],"additionalProperties":false}},"handle_relinquish":{"description":"Handles to hand to the Wikidata sync, while Wikidata lists them.","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"type":"string","description":"The handle as stored."}},"required":["platform_id","value"],"additionalProperties":false}},"handle_release":{"description":"Values held back from the sync that it may add again.","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"type":"string","description":"The handle as stored."}},"required":["platform_id","value"],"additionalProperties":false}},"vhid":{"type":"string","description":"The entry’s VHID (11 digits). Each entry at most once in a batch."},"base_revision_id":{"description":"The revision the change was made against (`baseRevisionId` of `GET /api/editor/{vhid}`): needed for a live edit, unless it is handle ops alone.","type":"string"},"summary":{"description":"A live edit’s why, shown in the history. Defaults to the batch’s `summary`.","type":"string","maxLength":500},"payload":{"description":"A proposal may nest its change under `payload`, as at `POST /api/entity/{vhid}/proposals`.","$ref":"#/components/schemas/EditPayload"},"qid_link":{"description":"A proposal’s Wikidata link, as at the single route.","type":"object","properties":{"qid":{"type":["string","null"]},"adopt":{"type":"array","items":{"type":"string"}}},"required":["qid"],"additionalProperties":false}},"required":["vhid"],"additionalProperties":false,"description":"One edit: its entry’s `vhid` beside exactly the body the single route takes — the editor’s live save for `live`, `POST /api/entity/{vhid}/proposals`’s for `propose`."}}},"required":["mode","edits"],"additionalProperties":false},"example":{"mode":"propose","edits":[{"vhid":"53500361835","payload":{"subtitle":"Founder of Verified Handles"},"base_revision_id":"1"},{"vhid":"00000000000","payload":{"subtitle":"No such entry"}}]}}}},"responses":{"200":{"description":"Each edit’s outcome, in request order (one at a time, or an atomic batch that was made whole).","headers":{"Idempotency-Replayed":{"description":"`true` when this is the stored answer to an earlier request with the same Idempotency-Key: nothing was written now.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"batch_id":{"type":"string","description":"This batch, as its audit rows name it (`detail.batch`)."},"mode":{"type":"string","enum":["live","propose"]},"atomic":{"type":"boolean"},"succeeded":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many edits were made or filed."},"failed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many were refused."},"results":{"type":"array","items":{"$ref":"#/components/schemas/BatchEditResult"}}},"required":["batch_id","mode","atomic","succeeded","failed","results"],"additionalProperties":false},"examples":{"proposals":{"summary":"Two proposals at once, one to an entry that takes none from this account","description":"POST /api/entity/batch","value":{"batch_id":"1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed","mode":"propose","atomic":false,"succeeded":1,"failed":1,"results":[{"vhid":"53500361835","status":"ok","submission_id":"41"},{"vhid":"00000000000","status":"error","error":"not_found","http_status":404}]}},"liveRefused":{"summary":"Live edits from an account that may not edit the entry live: refused edit by edit","description":"POST /api/entity/batch","value":{"batch_id":"1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed","mode":"live","atomic":false,"succeeded":0,"failed":1,"results":[{"vhid":"53500361835","status":"error","error":"forbidden","http_status":403}]}}}}}},"400":{"description":"The request is not a batch: `bad_json`, `bad_mode` (not `live` or `propose`), `no_edits`, `too_many_edits` (more than 25), `bad_edit` (an edit with no `vhid`), `duplicate_vhid` (an entry twice), or `bad_payload` / `summary_too_long` for `atomic` or `summary`. Nothing ran. Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one. Or the Idempotency-Key is not 1–255 printable ASCII characters (`bad_idempotency_key`).","content":{"application/json":{"schema":{"anyOf":[{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["bad_json","bad_payload","bad_mode","no_edits","too_many_edits","bad_edit","duplicate_vhid","summary_too_long"],"description":"What is wrong with the batch itself."},"index":{"description":"With `bad_edit`: the edit with no `vhid`.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"vhid":{"description":"With `duplicate_vhid`: the entry named twice.","type":"string"},"max":{"description":"With `too_many_edits`: the most a batch takes.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"field":{"description":"With `bad_payload`: the field of the wrong type (`atomic`, `summary`).","type":"string"}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/ApiKeyRefusal"}]},{"type":"object","properties":{"error":{"type":"string","enum":["bad_idempotency_key","idempotency_key_reused","idempotency_key_in_progress"],"description":"What was wrong with the Idempotency-Key."},"message":{"type":"string","description":"The refusal in words."}},"required":["error","message"],"additionalProperties":false}]},"examples":{"empty":{"summary":"No edits","description":"POST /api/entity/batch","value":{"error":"no_edits"}}}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"409":{"description":"The first request with this Idempotency-Key is still running (`idempotency_key_in_progress`): try again after `Retry-After` seconds.","headers":{"Retry-After":{"description":"With `idempotency_key_in_progress`: seconds to wait.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["bad_idempotency_key","idempotency_key_reused","idempotency_key_in_progress"],"description":"What was wrong with the Idempotency-Key."},"message":{"type":"string","description":"The refusal in words."}},"required":["error","message"],"additionalProperties":false}}}},"413":{"description":"The body is over 25 × 512 KiB. (One edit over 512 KiB is that edit’s `too_large`, 413.)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["too_large"],"description":"The body is too large."}},"required":["error"],"additionalProperties":false}}}},"422":{"description":"An atomic batch was refused whole: every refused edit named, the rest `not_applied`. Nothing was written. Or this Idempotency-Key was used for a different request in the last 24 hours (`idempotency_key_reused`): nothing was written.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","const":"batch_failed"},"mode":{"type":"string","enum":["live","propose"]},"atomic":{"type":"boolean","const":true},"failed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many edits were refused."},"results":{"type":"array","items":{"$ref":"#/components/schemas/BatchEditResult"}}},"required":["error","mode","atomic","failed","results"],"additionalProperties":false},{"type":"object","properties":{"error":{"type":"string","enum":["bad_idempotency_key","idempotency_key_reused","idempotency_key_in_progress"],"description":"What was wrong with the Idempotency-Key."},"message":{"type":"string","description":"The refusal in words."}},"required":["error","message"],"additionalProperties":false}]},"examples":{"atomic":{"summary":"All or nothing: one edit refused, so none is made","description":"POST /api/entity/batch","value":{"error":"batch_failed","mode":"propose","atomic":true,"failed":1,"results":[{"vhid":"53500361835","status":"not_applied"},{"vhid":"10000000001","status":"error","error":"bad_payload","http_status":400}]}}}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":"propose","x-read-only-key":false,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/entity/description-preview":{"post":{"operationId":"previewDescription","tags":["Proposing changes"],"summary":"See a description as it would be stored","description":"The HTML a save would store for this description, and nothing written: the same steps a live edit or an approved proposal runs (`[[VHID]]` and `[[short]]` links made into links to the entry, `[url label]` into a link, then the site’s HTML allowlist, which drops a script, an event handler and any tag or attribute it does not keep). Console → Batch edit shows a new description with it before anything is sent. `html` is null when nothing would be left, which a save stores as no description. Counts as one change against the rate limit, as the writes it previews.","parameters":[],"requestBody":{"description":"The description, as an edit would send it: HTML, or the `[[…]]` and `[url label]` link forms.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","description":"Up to 64 KiB in UTF-8, as a saved description."}},"required":["description"],"additionalProperties":false},"example":{"description":"<p>See <a href=\"https://example.org/\" onclick=\"steal()\">our site</a>.</p><script>alert(1)</script>"}}}},"responses":{"200":{"description":"The description as it would be stored.","content":{"application/json":{"schema":{"type":"object","properties":{"html":{"description":"The stored HTML; null when nothing would be left.","type":["string","null"]}},"required":["html"],"additionalProperties":false},"examples":{"sanitised":{"summary":"A link kept, its onclick and a script dropped","description":"POST /api/entity/description-preview","value":{"html":"<p>See <a href=\"https://example.org/\">our site</a>.</p>"}},"empty":{"summary":"Nothing left once the script is dropped","description":"POST /api/entity/description-preview","value":{"html":null}}}}}},"400":{"description":"The body is not JSON (`bad_json`), `description` is not a string (`bad_payload`), or it is over 64 KiB (`description_too_long`). Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["bad_json","bad_payload","description_too_long"],"description":"What is wrong with the request."},"field":{"description":"With `bad_payload`: `description`, which must be a string.","type":"string"}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]},"examples":{"signed-out":{"summary":"With no key","description":"POST /api/entity/description-preview","value":{"error":"auth_required"}}}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"413":{"description":"The body is over 512 KiB.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["too_large"],"description":"The body is too large."}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":"propose","x-read-only-key":false,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key is `no-store` with `Vary: Authorization`; to a signed-in session, `private, no-store`."},"x-cors":false}},"/api/media/library":{"get":{"operationId":"searchImageLibrary","tags":["Proposing changes"],"summary":"Find an image the site already holds","description":"The image picker’s “On Verified Handles” tab: images the site holds that may go on an entry (never a withdrawn one), found by the entries that show or showed them, a file name, a credit or licence, or `#<id>`. 24 to a page, newest first. Each says its licence and credit and which entries use it; a deleted entry is never named. Fewer than 2 characters finds nothing. Counted against the same per-account limit as changes, 60 a minute.","parameters":[{"name":"q","in":"query","required":true,"description":"What to look for, 2 to 200 characters.","schema":{"type":"string","maxLength":200},"example":"james"},{"name":"offset","in":"query","required":false,"description":"Results to skip; clamped to 10,000.","schema":{"default":0,"type":"integer","minimum":0,"maximum":10000}}],"responses":{"200":{"description":"A page of images.","content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string"},"total":{"type":"integer","minimum":0,"maximum":9007199254740991},"next_offset":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/LibraryImage"}}},"required":["query","total","next_offset","results"],"additionalProperties":false},"examples":{"short":{"summary":"Too short to look for","description":"GET /api/media/library?q=j","value":{"query":"j","total":0,"next_offset":null,"results":[]}}}}}},"400":{"description":"The text is over 200 characters (`query_too_long`). Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["query_too_long"],"description":"The text is over 200 characters."}},"required":["error"],"additionalProperties":{}},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/media/commons/search":{"get":{"operationId":"searchCommons","tags":["Proposing changes"],"summary":"Search Wikimedia Commons for an image","description":"The image picker’s Commons tab: up to 24 image files on Wikimedia Commons, each with a signed `token` that names it to the site. It asks Commons; when Commons does not answer in time, no results. A file name (with or without `File:`) also finds that exact file, first, even before Commons’ search has caught up with it. Files withdrawn or restricted here are left out. Fewer than 2 characters asks nothing. 60 a minute per account.","parameters":[{"name":"q","in":"query","required":true,"description":"What to look for, 2 to 200 characters.","schema":{"type":"string","maxLength":200},"example":"Ada Lovelace"}],"responses":{"200":{"description":"The files.","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/CommonsHit"}}},"required":["results"],"additionalProperties":false},"examples":{"short":{"summary":"Too short to ask Commons","description":"GET /api/media/commons/search?q=a","value":{"results":[]}}}}}},"400":{"description":"The text is over 200 characters (`query_too_long`). Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["query_too_long"],"description":"The text is over 200 characters."}},"required":["error"],"additionalProperties":{}},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/media/commons":{"post":{"operationId":"pickCommonsImage","tags":["Proposing changes"],"summary":"Choose a Commons file","description":"Turns a search hit’s `token` into an image the site holds, so a proposal can name it (`image_id`). The same file picked again answers the same image. It takes the token, never a file name: only a file a search found can be chosen.","parameters":[],"requestBody":{"description":"The hit’s token.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"],"additionalProperties":false},"example":{"token":"not-a-token"}}}},"responses":{"200":{"description":"Held: its id.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"mediaId":{"type":"string","pattern":"^\\d+$"},"filename":{"type":"string","description":"The file’s name, as Commons spells it."}},"required":["ok","mediaId","filename"],"additionalProperties":false}}}},"400":{"description":"Not JSON (`bad_json`), no token (`bad_payload`), or the token is forged or expired (`bad_token`). Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["bad_json","bad_payload","bad_token"],"description":"`bad_json`, `bad_payload` (no token), `bad_token` (forged or expired)."}},"required":["error"],"additionalProperties":{}},{"$ref":"#/components/schemas/ApiKeyRefusal"}]},"examples":{"bad-token":{"summary":"A token no search gave","description":"POST /api/media/commons","value":{"error":"bad_token"}}}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"409":{"description":"The file is withdrawn here (`image_withdrawn`), or restricted (`image_unavailable`).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["image_withdrawn","image_unavailable"],"description":"Why not."}},"required":["error"],"additionalProperties":{}}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/user/prefs":{"get":{"operationId":"getPrefs","tags":["Your account"],"summary":"Read your preferences","description":"The preferences of the account the key belongs to. Signed out, it still answers 200, with every value null, so a client need not check first.","parameters":[],"responses":{"200":{"description":"The preferences.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Prefs"},"examples":{"signed-in":{"summary":"With a key","description":"GET /api/user/prefs","value":{"signedIn":true,"csrf":"6a1f…","copyHandleOnly":false,"saveRecentSearches":true}},"signed-out":{"summary":"Signed out","description":"GET /api/user/prefs","value":{"signedIn":false,"csrf":null,"copyHandleOnly":null,"saveRecentSearches":null}}}}}},"400":{"description":"Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{},{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"optional","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false},"patch":{"operationId":"updatePrefs","tags":["Your account"],"summary":"Change your preferences","description":"Changes the preferences sent, each on its own; the others stay as they are. The body may be at most 1 KiB.","parameters":[],"requestBody":{"description":"The preferences to change.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrefsChange"},"example":{"copyHandleOnly":true}}}},"responses":{"200":{"description":"Changed.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true}},"required":["ok"],"additionalProperties":false},"examples":{"copy":{"summary":"Copy handles bare","description":"PATCH /api/user/prefs","value":{"ok":true}}}}}},"400":{"description":"The body is not JSON, names no preference, or gives one the wrong type. Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["bad_json","bad_value"],"description":"`bad_json`: the body is not JSON. `bad_value`: no preference, or one of the wrong type."}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/ApiKeyRefusal"}]},"examples":{"bad":{"summary":"A value of the wrong type","description":"PATCH /api/user/prefs","value":{"error":"bad_value"}}}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"413":{"description":"The body is over 1 KiB.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["too_large"],"description":"The body is too large."}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/api/v1/entities/{vhid}":{"get":{"operationId":"getEntity","tags":["Entries"],"summary":"Get an entry","description":"Everything the entry’s page shows, as data: its title, description, images with their rights, and handles. It is built by the same code as the page, so the two never disagree. An old name or a merged entry’s key answers with the entry it now leads to, without a redirect; `vhid` and `url` say which entry that is. The same body is served at `/{key}.json`.\n\nAny origin may read it: `Access-Control-Allow-Origin: *`, methods GET and OPTIONS, no credentials. An `OPTIONS` preflight answers 204. No key is needed. A request that sends one (`Authorization: Bearer`) has it checked, so a wrong, revoked or expired key is refused 401, and is counted against the key’s read limit; it gets the same answer, marked `no-store`.","parameters":[{"name":"vhid","in":"path","required":true,"description":"The entry’s VHID (11 digits), its short name, or one of its old names. Short names are matched exactly.","schema":{"type":"string","minLength":1,"maxLength":255},"example":"53500361835"}],"responses":{"200":{"description":"The entry.","headers":{"ETag":{"description":"The version of this representation.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Entity"},"examples":{"byShort":{"summary":"An entry by its short name","description":"GET /api/v1/entities/james","value":{"vhid":"53500361835","short":"james","title":"James Haworth","subtitle":null,"entity_type":"person","entity_kind":"person","verified":true,"description":null,"description_source":null,"description_license":null,"url":"https://verifiedhandles.org/james","image":"/img/w/480q75/3","image_rights":{"source":"upload","license":null,"license_name":null,"license_url":null,"freely_licensed":null,"credit":null,"source_page":null,"details_url":"https://verifiedhandles.org/james/image"},"images":[{"url":"/img/w/480q75/3","main":true,"image_rights":{"source":"upload","license":null,"license_name":null,"license_url":null,"freely_licensed":null,"credit":null,"source_page":null,"details_url":"https://verifiedhandles.org/james/image"},"link":null}],"handles":[{"platform":"twitter","name":"X (Twitter)","category":"social","value":"james","display":null,"url":"https://x.com/james","verified":false},{"platform":"linkedin","name":"LinkedIn","category":"social","value":"james-haworth","display":null,"url":"https://www.linkedin.com/in/james-haworth","verified":false},{"platform":"instagram","name":"Instagram","category":"social","value":"jameshaworth","display":null,"url":"https://instagram.com/jameshaworth","verified":false},{"platform":"email","name":"Email","category":"messaging","value":null,"display":null,"url":null,"verified":false}]}}}}}},"304":{"description":"Unchanged since the ETag sent in `If-None-Match`. No body."},"404":{"description":"No public entry answers to that key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"},"examples":{"notFound":{"summary":"A key nothing answers to","description":"GET /api/v1/entities/no-such-entry","value":{"error":"not_found"}}}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"ignored","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=60, s-maxage=2592000, stale-while-revalidate=86400, stale-if-error=604800","surrogate-keys":["entity:<vhid>"],"etag":true,"notes":"Shared caches may keep it for 30 days; an edit to the entry purges it at once (by its `entity:<vhid>` surrogate key). Browsers keep it for a minute. A strong ETag: send it back as `If-None-Match` and an unchanged entry answers 304, with no body."},"x-cors":true}},"/api/v1/entities":{"get":{"operationId":"getEntities","tags":["Entries"],"summary":"Get many entries at once","description":"Up to 50 entries in one request: the batch form of `GET /api/v1/entities/{vhid}`. Each key is answered exactly as that route answers it, in the order asked: the entry (`status: \"ok\"`, the same body), or `status: \"not_found\"` where it would answer 404. A deleted entry and a key that names nothing look the same. An old name or a merged entry’s key answers with the entry it now leads to; `entity.vhid` says which. A key repeated is answered once. The answer is 200 whatever each key answers: only the request itself is refused. A Wikidata QID is not a key here, as it is not at the single read: `GET /api/v1/search?q=Q42` finds the entries linked to an item. A key that contains a comma (only an old name can) cannot be asked for in a batch: use the single read.\n\n**Rate limit.** Each key counts as one read against the IP address’s budget, so 50 keys cost what 50 single reads cost, and the request is refused (429) when the budget cannot cover it. A response the edge already holds costs nothing.\n\nAny origin may read it: `Access-Control-Allow-Origin: *`, methods GET and OPTIONS, no credentials. An `OPTIONS` preflight answers 204. No key is needed. A request that sends one (`Authorization: Bearer`) has it checked, so a wrong, revoked or expired key is refused 401, and is counted against the key’s read limit; it gets the same answer, marked `no-store`.","parameters":[{"name":"ids","in":"query","required":true,"description":"The keys, comma-separated: each a VHID, a short name or an old name, as `{vhid}` takes. Each is trimmed; empty ones are dropped and a repeat counts once. At most 50 keys and 4096 characters.","schema":{"type":"string","minLength":1,"maxLength":4096},"example":"james,53500361835,Verified_Handles"}],"responses":{"200":{"description":"Each key’s answer.","headers":{"ETag":{"description":"The version of this answer.","schema":{"type":"string"}},"Surrogate-Key":{"description":"Every answered entry’s key.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityBatch"},"examples":{"twoKeys":{"summary":"One entry by its short name and by its VHID: each key answered","description":"GET /api/v1/entities?ids=james,53500361835","value":{"results":[{"key":"james","status":"ok","entity":{"vhid":"53500361835","short":"james","title":"James Haworth","subtitle":null,"entity_type":"person","entity_kind":"person","verified":true,"description":null,"description_source":null,"description_license":null,"url":"https://verifiedhandles.org/james","image":"/img/w/480q75/3","image_rights":{"source":"upload","license":null,"license_name":null,"license_url":null,"freely_licensed":null,"credit":null,"source_page":null,"details_url":"https://verifiedhandles.org/james/image"},"images":[{"url":"/img/w/480q75/3","main":true,"image_rights":{"source":"upload","license":null,"license_name":null,"license_url":null,"freely_licensed":null,"credit":null,"source_page":null,"details_url":"https://verifiedhandles.org/james/image"},"link":null}],"handles":[{"platform":"twitter","name":"X (Twitter)","category":"social","value":"james","display":null,"url":"https://x.com/james","verified":false},{"platform":"linkedin","name":"LinkedIn","category":"social","value":"james-haworth","display":null,"url":"https://www.linkedin.com/in/james-haworth","verified":false},{"platform":"instagram","name":"Instagram","category":"social","value":"jameshaworth","display":null,"url":"https://instagram.com/jameshaworth","verified":false},{"platform":"email","name":"Email","category":"messaging","value":null,"display":null,"url":null,"verified":false}]}},{"key":"53500361835","status":"ok","entity":{"vhid":"53500361835","short":"james","title":"James Haworth","subtitle":null,"entity_type":"person","entity_kind":"person","verified":true,"description":null,"description_source":null,"description_license":null,"url":"https://verifiedhandles.org/james","image":"/img/w/480q75/3","image_rights":{"source":"upload","license":null,"license_name":null,"license_url":null,"freely_licensed":null,"credit":null,"source_page":null,"details_url":"https://verifiedhandles.org/james/image"},"images":[{"url":"/img/w/480q75/3","main":true,"image_rights":{"source":"upload","license":null,"license_name":null,"license_url":null,"freely_licensed":null,"credit":null,"source_page":null,"details_url":"https://verifiedhandles.org/james/image"},"link":null}],"handles":[{"platform":"twitter","name":"X (Twitter)","category":"social","value":"james","display":null,"url":"https://x.com/james","verified":false},{"platform":"linkedin","name":"LinkedIn","category":"social","value":"james-haworth","display":null,"url":"https://www.linkedin.com/in/james-haworth","verified":false},{"platform":"instagram","name":"Instagram","category":"social","value":"jameshaworth","display":null,"url":"https://instagram.com/jameshaworth","verified":false},{"platform":"email","name":"Email","category":"messaging","value":null,"display":null,"url":null,"verified":false}]}}]}}}}}},"304":{"description":"Unchanged since the ETag sent in `If-None-Match`. No body."},"400":{"description":"`ids_required`: no keys. `too_many_ids`: more than `max` keys after removing repeats. `ids_too_long`: `ids` is over `max` characters.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["ids_required"],"description":"No keys were given."}},"required":["error"],"additionalProperties":false},{"type":"object","properties":{"error":{"type":"string","enum":["too_many_ids","ids_too_long"],"description":"Too many keys, or too long a list."},"max":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The limit that was passed."}},"required":["error","max"],"additionalProperties":false}]},"examples":{"tooMany":{"summary":"More keys than a batch takes","description":"GET /api/v1/entities?ids=k0,k1,k2,k3,k4,k5,k6,k7,k8,k9,k10,k11,k12,k13,k14,k15,k16,k17,k18,k19,k20,k21,k22,k23,k24,k25,k26,k27,k28,k29,k30,k31,k32,k33,k34,k35,k36,k37,k38,k39,k40,k41,k42,k43,k44,k45,k46,k47,k48,k49,k50","value":{"error":"too_many_ids","max":50}},"empty":{"summary":"No keys","description":"GET /api/v1/entities?ids=","value":{"error":"ids_required"}}}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"ignored","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=60, s-maxage=2592000, stale-while-revalidate=86400, stale-if-error=604800","surrogate-keys":["entity:<vhid>"],"etag":true,"notes":"When every key answers an entry, shared caches may keep it for 30 days, as the single read: it carries the `entity:<vhid>` surrogate key of every entry in it, so an edit to any one of them purges it at once. When any key answers `not_found`, it is `public, max-age=0, s-maxage=60, stale-while-revalidate=60` instead: a key that names nothing has nothing to be purged by when it starts to. A strong ETag, from every answer in it: send it back as `If-None-Match` and an unchanged batch answers 304, with no body. The keys are not reordered: the same keys in another order are another URL, cached apart, so ask in a stable order."},"x-cors":true}},"/api/v1/entities/{vhid}/handles":{"get":{"operationId":"getEntityHandles","tags":["Entries"],"summary":"Get an entry’s handles","description":"Just the entry’s handles, in the order its page shows them: the same list as `handles` in the entry. An email address is listed with its value, label and URL all null.\n\nAny origin may read it: `Access-Control-Allow-Origin: *`, methods GET and OPTIONS, no credentials. An `OPTIONS` preflight answers 204. No key is needed. A request that sends one (`Authorization: Bearer`) has it checked, so a wrong, revoked or expired key is refused 401, and is counted against the key’s read limit; it gets the same answer, marked `no-store`.","parameters":[{"name":"vhid","in":"path","required":true,"description":"The entry’s VHID (11 digits), its short name, or one of its old names. Short names are matched exactly.","schema":{"type":"string","minLength":1,"maxLength":255},"example":"53500361835"}],"responses":{"200":{"description":"The entry’s handles.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityHandles"},"examples":{"byVhid":{"summary":"An entry’s handles, by VHID","description":"GET /api/v1/entities/53500361835/handles","value":{"vhid":"53500361835","handles":[{"platform":"twitter","name":"X (Twitter)","category":"social","value":"james","display":null,"url":"https://x.com/james","verified":false},{"platform":"linkedin","name":"LinkedIn","category":"social","value":"james-haworth","display":null,"url":"https://www.linkedin.com/in/james-haworth","verified":false},{"platform":"instagram","name":"Instagram","category":"social","value":"jameshaworth","display":null,"url":"https://instagram.com/jameshaworth","verified":false},{"platform":"email","name":"Email","category":"messaging","value":null,"display":null,"url":null,"verified":false}]}}}}}},"404":{"description":"No public entry answers to that key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"ignored","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=60, s-maxage=2592000, stale-while-revalidate=86400, stale-if-error=604800","surrogate-keys":["entity:<vhid>"],"etag":false,"notes":"Shared caches may keep it for 30 days; an edit to the entry purges it at once (by its `entity:<vhid>` surrogate key). Browsers keep it for a minute."},"x-cors":true}},"/api/v1/entities/{vhid}/history":{"get":{"operationId":"getEntityHistory","tags":["History"],"summary":"Get an entry’s revision history","description":"The entry’s public revision history, newest first, a page at a time: the history kept from the old wiki and every edit made since. Hidden and deleted revisions are never listed, and an editor is named only if they chose to be. A merged entry’s key answers with that entry’s own history, and `merged_into` names the entry it became. An entry whose history has been hidden answers 404.\n\nAny origin may read it: `Access-Control-Allow-Origin: *`, methods GET and OPTIONS, no credentials. An `OPTIONS` preflight answers 204. No key is needed. A request that sends one (`Authorization: Bearer`) has it checked, so a wrong, revoked or expired key is refused 401, and is counted against the key’s read limit; it gets the same answer, marked `no-store`.","parameters":[{"name":"vhid","in":"path","required":true,"description":"The entry’s VHID (11 digits), its short name, or one of its old names. Short names are matched exactly.","schema":{"type":"string","minLength":1,"maxLength":255},"example":"53500361835"},{"name":"limit","in":"query","required":false,"description":"How many revisions a page holds. Out of range is clamped; anything else non-numeric reads as the default.","schema":{"default":100,"type":"integer","minimum":1,"maximum":200}},{"name":"offset","in":"query","required":false,"description":"How many revisions to skip. Clamped to 0–10,000.","schema":{"default":0,"type":"integer","minimum":0,"maximum":10000}}],"responses":{"200":{"description":"A page of the history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityHistory"},"examples":{"firstPage":{"summary":"The first page, two revisions to a page","description":"GET /api/v1/entities/james/history?limit=2","value":{"vhid":"53500361835","limit":2,"offset":0,"next_offset":null,"history":[{"ref_id":"r1","kind":"forward","ts":"2026-09-19T12:00:00.000Z","actor":null,"summary":null,"is_bot":false,"source":"import","hidden":false,"deleted":false,"proposer":null}]}}}}}},"404":{"description":"No public entry answers to that key, or its history is hidden.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"ignored","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=0, s-maxage=300, stale-while-revalidate=300","surrogate-keys":["entity:<vhid>","entity:<merged_into>"],"etag":false,"notes":"Shared caches keep it for 5 minutes, and an edit purges it at once. Browsers revalidate on every request. A merged entry’s history also carries the surrogate key of the entry it was merged into."},"x-cors":true}},"/api/v1/search":{"get":{"operationId":"search","tags":["Search"],"summary":"Search the entries","description":"Full-text search over every public entry’s title, short name, subtitle and description, best match first. Two kinds of query are looked up instead of searched: a Wikidata QID (`Q42`) finds the entries linked to that item, and a VHID finds its entry and the entries that link to it. Each of those is one page: `offset` is ignored and `next_offset` is null.\n\nAny origin may read it: `Access-Control-Allow-Origin: *`, methods GET and OPTIONS, no credentials. An `OPTIONS` preflight answers 204. No key is needed. A request that sends one (`Authorization: Bearer`) has it checked, so a wrong, revoked or expired key is refused 401, and is counted against the key’s read limit; it gets the same answer, marked `no-store`.","parameters":[{"name":"q","in":"query","required":true,"description":"What to search for: words, a QID or a VHID. Trimmed and cut to 100 characters. Empty searches nothing.","schema":{"type":"string"},"example":"james"},{"name":"limit","in":"query","required":false,"description":"How many results a page holds. Out of range is clamped; anything else non-numeric reads as the default.","schema":{"default":20,"type":"integer","minimum":1,"maximum":50}},{"name":"offset","in":"query","required":false,"description":"How many results to skip. Clamped to 0–10,000. Ignored for a QID or a VHID.","schema":{"default":0,"type":"integer","minimum":0,"maximum":10000}}],"responses":{"200":{"description":"A page of results; for an empty `q`, an empty answer with no paging fields.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SearchResponse"},{"$ref":"#/components/schemas/EmptySearch"}]},"examples":{"words":{"summary":"A search for a name","description":"GET /api/v1/search?q=james&limit=2","value":{"query":"james","total":1,"limit":2,"offset":0,"next_offset":null,"results":[{"vhid":"53500361835","short":"james","title":"James Haworth","subtitle":null,"verified":true,"snippet":null,"highlighted":false,"url":"https://verifiedhandles.org/james"}]}}}}}},"422":{"description":"The query is one of the phrases the site does not search for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Blocked"},"examples":{"blocked":{"summary":"A query the site does not search for","description":"GET /api/v1/search?q=sadist","value":{"error":"blocked","query":"sadist"}}}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"ignored","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=0, s-maxage=60, stale-while-revalidate=60","surrogate-keys":["listing"],"etag":false,"notes":"Shared caches keep a result for a minute; blocking or unblocking an entry purges every result at once (the `listing` surrogate key). An empty query is not cached at all, nor is a refused one."},"x-cors":true}},"/api/v1/handles":{"get":{"operationId":"findByHandle","tags":["Entries"],"summary":"Find the entries that list a handle","description":"Which public entries list this handle: “is x.com/someone an official account, and whose?”. Ask by `platform` and `value`, or by `url`, a profile’s address, which is read as the editor reads a pasted one: the platform whose address it is, and what follows it (`https://x.com/someone?s=20` is `twitter`, `someone`). The value is matched as the site stores and compares a handle: trimmed, case ignored on a platform that ignores it, a leading `@` optional on a platform shown with one, and a website completed to its `https://` address. An entry that lists the handle twice (`@x` and `x`) is answered once. A deleted entry, and a handle on a platform the site does not show, are never answered, nor is a Facebook value that is one of Facebook’s own path words (`pg`, `groups`, `?fref=ts`: no one’s account, and not shown on any entry). Email addresses are not looked up. Nothing found is a 200 with an empty `results`.\n\nAny origin may read it: `Access-Control-Allow-Origin: *`, methods GET and OPTIONS, no credentials. An `OPTIONS` preflight answers 204. No key is needed. A request that sends one (`Authorization: Bearer`) has it checked, so a wrong, revoked or expired key is refused 401, and is counted against the key’s read limit; it gets the same answer, marked `no-store`.","parameters":[{"name":"platform","in":"query","required":false,"description":"The platform’s id in the catalog (`twitter`, `instagram`, `youtube`, …). With `value`; not with `url`.","schema":{"type":"string"},"example":"twitter"},{"name":"value","in":"query","required":false,"description":"The handle, as it follows the platform’s address (`someone` or `@someone`). At most 1,000 characters.","schema":{"type":"string","maxLength":1000},"example":"james"},{"name":"url","in":"query","required":false,"description":"A profile’s address, instead of `platform` and `value`: `https://x.com/someone`. The scheme is optional; the query, the fragment and trailing slashes are ignored. At most 1,000 characters.","schema":{"type":"string","maxLength":1000},"example":"https://x.com/james"}],"responses":{"200":{"description":"The entries that list the handle, perhaps none.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandleLookupResponse"},"examples":{"byValue":{"summary":"A handle by platform and value, in another case and with its @","description":"GET /api/v1/handles?platform=twitter&value=@James","value":{"platform":"twitter","value":"James","results":[{"vhid":"53500361835","title":"James Haworth","url":"https://verifiedhandles.org/james","value":"james","verified":false}]}},"byUrl":{"summary":"The same handle by its profile’s address","description":"GET /api/v1/handles?url=https://x.com/james?s=20","value":{"platform":"twitter","value":"james","results":[{"vhid":"53500361835","title":"James Haworth","url":"https://verifiedhandles.org/james","value":"james","verified":false}]}},"none":{"summary":"A handle no entry lists","description":"GET /api/v1/handles?platform=twitter&value=nobody-lists-this","value":{"platform":"twitter","value":"nobody-lists-this","results":[]}}}}}},"400":{"description":"`platform_required`: neither a platform nor a url. `url_or_platform`: both. `unknown_platform`: not a platform the site lists handles on; `platforms` lists the ones it does. `masked_platform`: `email`. `value_required`, `value_too_long`. `url_not_recognised`: no platform’s address is that one. Each with a `message` saying so.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["platform_required","url_or_platform","unknown_platform","masked_platform","value_required","value_too_long","url_not_recognised"],"description":"What is wrong with the request."},"message":{"type":"string","description":"The same, in words."},"platforms":{"description":"With `unknown_platform`: the platform ids a look-up takes.","type":"array","items":{"type":"string"}}},"required":["error","message"],"additionalProperties":false},"examples":{"unknownPlatform":{"summary":"A platform the site does not list","description":"GET /api/v1/handles?platform=myspace&value=tom","value":{"error":"unknown_platform","message":"\"myspace\" is not a platform the site lists handles on. `platforms` lists the ones it does.","platforms":["twitter"]}}}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"ignored","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=0, s-maxage=60, stale-while-revalidate=60","surrogate-keys":["listing","entity:<vhid>"],"etag":false,"notes":"Shared caches keep an answer for a minute, as search: an edit to an entry it lists purges it at once (each entry’s `entity:<vhid>` surrogate key), as does blocking or unblocking any entry (`listing`). An entry that adds the handle shows within the minute. A refused request is not cached."},"x-cors":true}},"/api/v1/openapi.json":{"get":{"operationId":"getOpenApi","tags":["About the API"],"summary":"Get this document","description":"This description of the public API, as OpenAPI 3.1. It is generated from the same registry the routes are tested against, so it changes when the API does.\n\nAny origin may read it: `Access-Control-Allow-Origin: *`, methods GET and OPTIONS, no credentials. An `OPTIONS` preflight answers 204. No key is needed. A request that sends one (`Authorization: Bearer`) has it checked, so a wrong, revoked or expired key is refused 401, and is counted against the key’s read limit; it gets the same answer, marked `no-store`.","parameters":[],"responses":{"200":{"description":"The OpenAPI document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiDocument"}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"ignored","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=3600","etag":false,"notes":"Anyone may keep it for an hour."},"x-cors":true}},"/api/wikidata/items/{qid}":{"get":{"operationId":"getWikidataItem","tags":["Proposing changes"],"summary":"Read a Wikidata item","description":"What a Wikidata item says, the way the Wikidata sync would store it: label, description, image and handles, with the entries already on it. For proposing what it says onto an entry. It asks Wikidata within a lookup budget per account and for the site, behind a short cache; over budget, or when Wikidata does not answer, a 503: try again in a minute.","parameters":[{"name":"qid","in":"path","required":true,"description":"The item, `Q<n>`.","schema":{"type":"string"},"example":"Q42"}],"responses":{"200":{"description":"The item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WikidataItem"},"examples":{"q42":{"summary":"An item","description":"GET /api/wikidata/items/Q42","value":{"qid":"Q42","label":"Douglas Adams","description":"English author and humourist (1952–2001)","image":{"filename":"Douglas adams portrait cropped.jpg","token":"eyJ…"},"handles":[{"platform_id":"website","value":"https://douglasadams.com/","value_norm":"https://douglasadams.com/"}],"entries_on_item":[]}}}}}},"400":{"description":"Not a QID (`bad_qid`). Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"error":{"type":"string","enum":["bad_qid"],"description":"Not a QID."}},"required":["error"],"additionalProperties":false},{"$ref":"#/components/schemas/ApiKeyRefusal"}]},"examples":{"bad":{"summary":"Not a QID","description":"GET /api/wikidata/items/42x","value":{"error":"bad_qid"}}}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"The account may not propose (`forbidden`). A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"Wikidata has no such item.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","const":"wikidata_no_item"},"qid":{"type":"string"}},"required":["error","qid"],"additionalProperties":false}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"Over the lookup budget, or Wikidata did not answer (JSON, with `Retry-After: 60`); or the site is down for maintenance (an HTML page).","headers":{"Retry-After":{"description":"Seconds to wait.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","const":"wikidata_busy"},"retry":{"type":"boolean","const":true}},"required":["error","retry"],"additionalProperties":false}}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":"propose","x-read-only-key":true,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/applications/check.json":{"get":{"operationId":"checkApplication","tags":["Applying for an entry"],"summary":"Check for an entry before applying","description":"Whether the site already has the entry you would apply for: the entry on a Wikidata item, and up to 5 entries whose names match a title. It reads only this site, never Wikidata.","parameters":[{"name":"qid","in":"query","required":false,"description":"A Wikidata item, `Q<n>`.","schema":{"type":"string"},"example":"Q7259"},{"name":"title","in":"query","required":false,"description":"A name. Fewer than 2 characters matches nothing; cut to 100.","schema":{"type":"string"},"example":"james"}],"responses":{"200":{"description":"What the site has.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplicationCheck"},"examples":{"james":{"summary":"By a name","description":"GET /applications/check.json?title=james","value":{"qid":null,"title":[{"vhid":"53500361835","title":"James Haworth","short":"james"}]}}}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{},{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"optional","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached, so an entry made a moment ago is found."},"x-cors":false}},"/applications/wikidata.json":{"get":{"operationId":"suggestFromWikidata","tags":["Applying for an entry"],"summary":"Find the Wikidata item to apply with","description":"Up to 6 Wikidata items whose names match, each with the entry already on it. It asks Wikidata, within a lookup budget per account and for the site; past the budget, or when Wikidata does not answer, `status` is `unavailable`. Fewer than 2 characters answers no items.","parameters":[{"name":"q","in":"query","required":true,"description":"A name.","schema":{"type":"string"},"example":"Ada Lovelace"}],"responses":{"200":{"description":"The items, or `unavailable`.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","const":"ok"},"hits":{"type":"array","items":{"type":"object","properties":{"qid":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"entry":{"anyOf":[{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string"}},"required":["vhid","short","title"],"additionalProperties":false},{"type":"null"}],"description":"The entry already on the item, or null."}},"required":["qid","label","description","entry"],"additionalProperties":false}}},"required":["status","hits"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","const":"unavailable"}},"required":["status"],"additionalProperties":false}]},"examples":{"short":{"summary":"Too short to look up","description":"GET /applications/wikidata.json?q=a","value":{"status":"ok","hits":[]}}}}}},"400":{"description":"Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/applications":{"post":{"operationId":"applyForEntry","tags":["Applying for an entry"],"summary":"Apply for an entry","description":"Files an application, which a reviewer decides; you are told by email, and can follow it at `/applications/{id}`. Check first with `GET /applications/check.json`: an item already on an entry is refused, as is an item Wikidata has merged into another (the message names the item it kept), and asking again about one you already have open answers that one (`?dup=1`). 10 a day. A refusal is the form again, as HTML, with every value kept and each problem beside its field.","parameters":[],"requestBody":{"description":"The application.","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/ApplicationForm"},"example":{"title":"Api Docs Application","relationship":"self","handle_platform":"twitter","handle_value":"apidocsapp","wikipedia_url":"https://en.wikipedia.org/wiki/Test","consent":"1"}}}},"responses":{"303":{"description":"Filed: on to it, `?sent=1`. Or you already have one open on that subject: on to that one, `?dup=1`.","headers":{"Location":{"description":"`/applications/<id>?sent=1`, or `?dup=1`.","schema":{"type":"string"}}}},"400":{"description":"The form again, as HTML, with what to fix. (A key with a cookie: `bearer_with_cookie`, JSON.)","content":{"text/html":{}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"Your account may not propose: a page that says so. (A read-only key, or a blocked account: JSON.)","content":{"text/html":{}}},"429":{"description":"10 applications in the last day already: the form again, saying so. Or too many changes in the last minute, as text, with `Retry-After`.","content":{"text/html":{}}},"503":{"description":"Applications are paused: a page that says so.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":"propose","x-read-only-key":false,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/applications/{id}/comments":{"post":{"operationId":"replyToMyApplication","tags":["Applying for an entry"],"summary":"Reply on your application","description":"Adds to the application’s conversation with its reviewers; a reply to a question (`needs_info`) puts it back in their queue. Back to the application, or `?error=<why>` (`empty`, `blocked` for blocked language, `closed` once it is decided).","parameters":[{"name":"id","in":"path","required":true,"description":"The application’s id.","schema":{"type":"string","pattern":"^\\d+$"},"example":"990000001"}],"requestBody":{"description":"The reply.","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"What to say, up to 2,000 characters."}},"required":["body"],"additionalProperties":false},"example":{"body":"The Wikipedia article was updated this week."}}}},"responses":{"303":{"description":"Back to the application’s conversation.","headers":{"Location":{"description":"`/applications/<id>#vh-thread-h`, or with `?error=<why>`.","schema":{"type":"string"}}}},"400":{"description":"Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"No such application, or not yours: the not-found page.","content":{"text/html":{}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/applications/{id}/retract":{"post":{"operationId":"retractMyApplication","tags":["Applying for an entry"],"summary":"Withdraw your application","description":"Withdraws it while it is still open. Back to the application, or `?error=cannot_retract` once it is decided.","parameters":[{"name":"id","in":"path","required":true,"description":"The application’s id.","schema":{"type":"string","pattern":"^\\d+$"},"example":"990000001"}],"requestBody":{"description":"Nothing: the address says which.","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{},"additionalProperties":false},"example":{}}}},"responses":{"303":{"description":"Back to the application.","headers":{"Location":{"description":"`/applications/<id>`, or with `?error=cannot_retract`.","schema":{"type":"string"}}}},"400":{"description":"Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"No such application, or not yours: the not-found page.","content":{"text/html":{}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/find":{"get":{"operationId":"consoleFindEntry","tags":["Entry manager"],"summary":"Find an entry by name, for a console form or the console search","description":"Up to 10 entries matching `find`: blocked ones too for an account with `manage_entries` or `suppress_history`, else what the public search finds. Each says whether its history is hidden and, to an account that may open its Wikidata page, whether the sync may read it now (linked to an item, neither protected nor claimed; false to anyone else): what the console search needs to offer the entry’s other pages. Empty `find`: none.","parameters":[{"name":"find","in":"query","required":false,"description":"Words in the title or short name, a VHID or a QID.","schema":{"type":"string"},"example":"James"}],"responses":{"200":{"description":"The matches.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"VHID":{"type":"string"},"Title":{"type":"string"},"short":{"type":["string","null"]},"blocked":{"type":"boolean"},"history_hidden":{"type":"boolean"},"syncable":{"type":"boolean"}},"required":["VHID","Title","short","blocked","history_hidden","syncable"],"additionalProperties":false}},"examples":{"empty":{"summary":"Nothing asked","description":"GET /management-console/find","value":[]}}}}},"302":{"description":"No key was sent: the console sends a browser to sign in. A script should send its key.","headers":{"Location":{"description":"/management-console/auth/login?next=<the path asked for>","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/submissions.json":{"get":{"operationId":"listMySubmissions","tags":["Your account"],"summary":"List what you have sent for review","description":"Your proposals, new entries, claims, reports and applications, newest first, 25 to a page: every one, or with `status=open` only those still waiting on a decision.","parameters":[{"name":"status","in":"query","required":false,"description":"`open`: only those waiting on a decision. Anything else: all of them.","schema":{"default":"all","type":"string","enum":["open","all"]}},{"name":"page","in":"query","required":false,"description":"The page, from 1; past 10,000 it is clamped.","schema":{"default":1,"type":"integer","minimum":1,"maximum":10000}}],"responses":{"200":{"description":"A page of your submissions.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","all"]},"page":{"type":"integer","minimum":1,"maximum":9007199254740991},"per_page":{"type":"number","const":25},"total":{"type":"integer","minimum":0,"maximum":9007199254740991},"has_next":{"type":"boolean"},"submissions":{"type":"array","items":{"$ref":"#/components/schemas/SubmissionBrief"}}},"required":["status","page","per_page","total","has_next","submissions"],"additionalProperties":false},"examples":{"open":{"summary":"Those still waiting","description":"GET /management-console/submissions.json?status=open","value":{"status":"open","page":1,"per_page":25,"total":1,"has_next":false,"submissions":[{"id":"990000002","kind":"new_entity","status":"pending","entry":null,"qid":null,"created_at":"2026-10-05T12:00:00.000Z","decided_at":null,"review_notes":null}]}}}}}},"302":{"description":"No key was sent: the console sends a browser to sign in. A script should send its key.","headers":{"Location":{"description":"/management-console/auth/login?next=<the path asked for>","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/submissions/{file}":{"get":{"operationId":"getMySubmission","tags":["Your account"],"summary":"Read one of your submissions, with its conversation","description":"One submission, what it asked for (`payload`, as it was sent) and its comments, oldest first. Its submitter and reviewers may read it; to anyone else it is 404, as a missing one is. A masked email the proposal keeps is in `payload.handles` as `{value: '', kept: true, ref}`, never its address; one it removes is left out. Its page shows such a removal as “Email: (an address)” to a submitter who may not edit the entry live.","parameters":[{"name":"file","in":"path","required":true,"description":"The submission’s id, then `.json`.","schema":{"type":"string","pattern":"^\\d{1,18}\\.json$"},"example":"990000002.json"}],"responses":{"200":{"description":"The submission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionDetail"},"examples":{"mine":{"summary":"A new entry you proposed","description":"GET /management-console/submissions/990000002.json","value":{"id":"990000002","kind":"new_entity","status":"pending","entry":null,"qid":null,"created_at":"2026-10-05T12:00:00.000Z","decided_at":null,"review_notes":null,"mine":true,"payload":{"title":"Api Docs Proposal","handles":[]},"comments":[]}}}}}},"302":{"description":"No key was sent: the console sends a browser to sign in. A script should send its key.","headers":{"Location":{"description":"/management-console/auth/login?next=<the path asked for>","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"404":{"description":"No such submission, or not one you may read.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["not_found"],"description":"Not found."}},"required":["error"],"additionalProperties":{}},"examples":{"missing":{"summary":"Not there","description":"GET /management-console/submissions/999999999.json","value":{"error":"not_found"}}}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/submissions/{id}/comments":{"post":{"operationId":"commentOnMySubmission","tags":["Your account"],"summary":"Comment on one of your submissions","description":"Adds a comment to the submission’s conversation, which its reviewers read. Answers as the page’s form does: back to the submission, `done=commented`, or `error=<why>` (`empty`, `blocked` for blocked language, `closed` once it is decided).","parameters":[{"name":"id","in":"path","required":true,"description":"The submission’s id.","schema":{"type":"string","pattern":"^\\d+$"},"example":"990000002"}],"requestBody":{"description":"The comment.","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"What to say, up to 2,000 characters."}},"required":["body"],"additionalProperties":false},"example":{"body":"The handles are on the subject’s own website."}}}},"responses":{"302":{"description":"No key was sent: the console sends a browser to sign in. A script should send its key.","headers":{"Location":{"description":"/management-console/auth/login?next=<the path asked for>","schema":{"type":"string"}}}},"303":{"description":"Done: back to /management-console/submissions/<id>, `done=commented` or `error=<why>`.","headers":{"Location":{"description":"/management-console/submissions/<id>, with the outcome in its query.","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`. A read-only key that tries to change something is refused first, as JSON: `{\"error\":\"read_only_key\"}`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"404":{"description":"No such submission, or not one you may comment on.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/submissions/{id}/retract":{"post":{"operationId":"retractMySubmission","tags":["Your account"],"summary":"Withdraw one of your submissions","description":"Withdraws a submission of yours that is still waiting on a decision: it leaves the queue, marked `retracted`. Back to the submission, `done=retracted`, or `error=cannot_retract` when it is not yours or already decided.","parameters":[{"name":"id","in":"path","required":true,"description":"The submission’s id.","schema":{"type":"string","pattern":"^\\d+$"},"example":"990000002"}],"requestBody":{"description":"Nothing: the address says which.","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{},"additionalProperties":false},"example":{}}}},"responses":{"302":{"description":"No key was sent: the console sends a browser to sign in. A script should send its key.","headers":{"Location":{"description":"/management-console/auth/login?next=<the path asked for>","schema":{"type":"string"}}}},"303":{"description":"Done: back to /management-console/submissions/<id>, `done=retracted` or `error=cannot_retract`.","headers":{"Location":{"description":"/management-console/submissions/<id>, with the outcome in its query.","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`. A read-only key that tries to change something is refused first, as JSON: `{\"error\":\"read_only_key\"}`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/account/prefs":{"post":{"operationId":"setMyAccountPrefs","tags":["Your account"],"summary":"Set your account’s preferences","description":"Sets every preference on the account page at once, as its form does: a box left out is switched off. `PATCH /api/user/prefs` changes one at a time instead.","parameters":[],"requestBody":{"description":"The preferences. `1` for on; absent for off.","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"copy_handle_only":{"description":"Copy a handle without its `@` or address.","type":"string","const":"1"},"email_notifications":{"description":"Email me when something I sent is decided.","type":"string","const":"1"},"save_recent_searches":{"description":"Keep my recent searches with my account.","type":"string","const":"1"},"name_visibility":{"description":"Who sees my name on what I did. Absent: unchanged.","type":"string","enum":["public","trusted","admins"]}},"additionalProperties":false},"example":{"copy_handle_only":"1","save_recent_searches":"1","name_visibility":"public"}}}},"responses":{"302":{"description":"Done: back to /management-console/account, `done=prefs`. (With no key and nobody signed in, a 302 to sign in instead.)","headers":{"Location":{"description":"/management-console/account, with the outcome in its query.","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`. A read-only key that tries to change something is refused first, as JSON: `{\"error\":\"read_only_key\"}`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/account/email-resume":{"post":{"operationId":"resumeMyEmail","tags":["Your account"],"summary":"Have the site email your address again","description":"Takes your own address off the email suppression list, after Amazon SES reported it as a permanent bounce or a spam complaint. Nothing changes when it is not on the list. The next bounce or complaint lists it again.","parameters":[],"requestBody":{"description":"Nothing but the CSRF token.","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{},"additionalProperties":false},"example":{}}}},"responses":{"302":{"description":"Done: back to /management-console/account, `done=email-resumed`. (With no key and nobody signed in, a 302 to sign in instead.)","headers":{"Location":{"description":"/management-console/account, with the outcome in its query.","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`. A read-only key that tries to change something is refused first, as JSON: `{\"error\":\"read_only_key\"}`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/account/data.json":{"get":{"operationId":"downloadMyData","tags":["Your account"],"summary":"Download everything the site holds about you","description":"Your account, preferences, submissions, comments, notes, API keys (never a secret), your entries in the audit log, the edits you made, proposed or hid, what you reviewed, claimed and uploaded, your permission history, your Wikidata changes, the emails queued to you and when your sessions began, as one JSON file. Each download is audited (row counts only). 5 an hour per account. A blocked account may download its own too.","parameters":[],"responses":{"200":{"description":"The file, with `Content-Disposition: attachment`.","headers":{"Content-Disposition":{"description":"`attachment; filename=\"verifiedhandles-data-<date>.json\"`.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountData"}}}},"302":{"description":"No key was sent: the console sends a browser to sign in. A script should send its key.","headers":{"Location":{"description":"/management-console/auth/login?next=<the path asked for>","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"429":{"description":"More than 5 downloads in the last hour: wait a while.","content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/account/delete":{"post":{"operationId":"deleteMyAccount","tags":["Your account"],"summary":"Delete your account","description":"Deletes the account at once and for good: every session and API key (this one too), the profile, preferences and unpublished submissions and unsaved drafts. What it added to entries stays on the record under an anonymous deleted account. A blocked account may delete itself. There is no undo and no dry run.","parameters":[],"requestBody":{"description":"Nothing: it is your own account.","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"302":{"description":"No key was sent: the console sends a browser to sign in. A script should send its key.","headers":{"Location":{"description":"/management-console/auth/login?next=<the path asked for>","schema":{"type":"string"}}}},"303":{"description":"Deleted: on to the page that confirms it. `?err=blocked_no_key` on the account page instead when a blocked account’s block could not be kept, and nothing was deleted.","headers":{"Location":{"description":"/management-console/account/deleted","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`. A read-only key that tries to change something is refused first, as JSON: `{\"error\":\"read_only_key\"}`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/account/api-keys.json":{"get":{"operationId":"listMyApiKeys","tags":["Your account"],"summary":"List your API keys","description":"Every key your account has made, live or not, newest first; `this_key` marks the one making the request. Never a secret. Any account may list its own, even one that may no longer make keys. `apps` are the apps connected to the account through OAuth: the live ones, then the 20 that ended most recently.","parameters":[],"responses":{"200":{"description":"Your keys and connected apps.","content":{"application/json":{"schema":{"type":"object","properties":{"sub":{"type":"string"},"keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyRow"}},"apps":{"type":"array","items":{"$ref":"#/components/schemas/ConnectedAppRow"}}},"required":["sub","keys","apps"],"additionalProperties":false}}}},"302":{"description":"No key was sent: the console sends a browser to sign in. A script should send its key.","headers":{"Location":{"description":"/management-console/auth/login?next=<the path asked for>","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/account/api-keys/{id}/revoke":{"post":{"operationId":"revokeMyApiKey","tags":["Your account"],"summary":"Revoke one of your API keys","description":"Revokes the key at once, audited: anything using it stops working. Revoking the key that makes the request is allowed, and is its last request. Back to the API keys page, `done=revoked`, or `err=gone` when it was not one of your live keys. A connected app is disconnected the same way, with its `key`: its tokens stop working at once (with `kind=app`, the page answers `done=disconnected` or `err=app_gone`).","parameters":[{"name":"id","in":"path","required":true,"description":"The key’s id (`id` in the list).","schema":{"type":"string","pattern":"^\\d+$"},"example":"1"}],"requestBody":{"description":"Nothing but, from the Connected apps list, which page to answer with: the address says which key.","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"kind":{"description":"`app`: a connected app is being disconnected, so the answer is `done=disconnected` or `err=app_gone`.","type":"string","const":"app"}},"additionalProperties":false},"example":{}}}},"responses":{"302":{"description":"No key was sent: the console sends a browser to sign in. A script should send its key.","headers":{"Location":{"description":"/management-console/auth/login?next=<the path asked for>","schema":{"type":"string"}}}},"303":{"description":"Done: back to /management-console/account/api-keys, `done=revoked` or `err=gone`.","headers":{"Location":{"description":"/management-console/account/api-keys, with the outcome in its query.","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`. A read-only key that tries to change something is refused first, as JSON: `{\"error\":\"read_only_key\"}`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/management-console/account/api-keys/{id}/activity.json":{"get":{"operationId":"myApiKeyActivity","tags":["Your account"],"summary":"What one of your API keys did","description":"Every change made with one of your keys (the audit log’s rows that name it), newest first, 50 a page. Changes, not reads: reading writes no row. Page with `before` (the `next` of the page before) and back with `after` (`prev`). Only your own keys: any other id is a 404, whoever holds it. No other capability is needed.","parameters":[{"name":"id","in":"path","required":true,"description":"The key’s id (`id` in the list).","schema":{"type":"string","pattern":"^\\d+$"},"example":"1"},{"name":"before","in":"query","required":false,"description":"Rows older than this one (a row id: `next`).","schema":{"type":"string"}},{"name":"after","in":"query","required":false,"description":"Rows newer than this one (a row id: `prev`).","schema":{"type":"string"}}],"responses":{"200":{"description":"The key and a page of its rows.","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"name":{"type":"string"},"prefix":{"type":"string"},"state":{"type":"string","enum":["live","revoked","expired"]}},"required":["id","name","prefix","state"],"additionalProperties":false},"rows":{"type":"array","items":{"$ref":"#/components/schemas/KeyActivityRow"}},"next":{"anyOf":[{"type":"string","pattern":"^\\d+$"},{"type":"null"}],"description":"Pass as `before` for older rows; null on the last page."},"prev":{"anyOf":[{"type":"string","pattern":"^\\d+$"},{"type":"null"}],"description":"Pass as `after` for newer rows; null on the first page."}},"required":["key","rows","next","prev"],"additionalProperties":false}}}},"302":{"description":"No key was sent: the console sends a browser to sign in. A script should send its key.","headers":{"Location":{"description":"/management-console/auth/login?next=<the path asked for>","schema":{"type":"string"}}}},"400":{"description":"The request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The key’s account lacks the capability, or is blocked: the body is the word `Forbidden`.","content":{"text/plain":{"schema":{"type":"string","const":"Forbidden"}}}},"404":{"description":"Not one of your keys.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["not_found"],"description":"Not found."}},"required":["error"],"additionalProperties":{}},"examples":{"not_mine":{"summary":"A key that is not yours","description":"GET /management-console/account/api-keys/999999999/activity.json","value":{"error":"not_found"}}}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"required","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached: it is about the caller. An answer to a key also carries `Vary: Authorization`."},"x-cors":false}},"/{key}/freshness":{"get":{"operationId":"getFreshness","tags":["Entries"],"summary":"When an entry last changed","description":"When the entry last changed, never cached: a cheap way to tell whether a copy you hold is stale. An old name answers for the entry it leads to. Each call counts as someone reading the entry, which helps decide what the Wikidata sync refreshes first; add `quiet` to a call that should not count.","parameters":[{"name":"key","in":"path","required":true,"description":"The entry’s VHID (11 digits), its short name, or one of its old names. Short names are matched exactly.","schema":{"type":"string","minLength":1,"maxLength":255},"example":"james"},{"name":"quiet","in":"query","required":false,"description":"Present (any value): do not count this call as a read.","schema":{"type":"string"},"example":"1"}],"responses":{"200":{"description":"When it last changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Freshness"},"examples":{"james":{"summary":"An entry","description":"GET /james/freshness?quiet=1","value":{"updated_at":"2026-09-19T12:00:00.000Z"}}}}}},"404":{"description":"No public entry answers to that key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"},"examples":{"missing":{"summary":"No such entry","description":"GET /no-such-entry-here/freshness","value":{"error":"not_found"}}}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{},{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"optional","x-rate-limit":null,"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached, so it is always current."},"x-cors":false}},"/{key}/history.json":{"get":{"operationId":"getEntityHistoryJson","tags":["History"],"summary":"Get an entry’s history, at its history page’s address","description":"The entry’s public revision history as its history page shows it: newest first, 50 to a page, by page number. `GET /api/v1/entities/{vhid}/history` gives the same revisions with `limit` and `offset` instead. An old name redirects (301) to the same address under the entry’s canonical key, query kept. A merged entry’s key answers with that entry’s own history, and `merged_into` names the entry it became. An entry whose history has been hidden answers 403 `history_hidden`, saying how to ask for it through the contact page.\n\nSame-origin only: it sends no CORS headers. A key is not needed. One that is sent is checked: a bad one is refused (400 or 401) before the route runs, and a good one makes the answer private (`Cache-Control: no-store`, `Vary: Authorization`).","parameters":[{"name":"key","in":"path","required":true,"description":"The entry’s VHID (11 digits), its short name, or one of its old names. Short names are matched exactly.","schema":{"type":"string","minLength":1,"maxLength":255},"example":"james"},{"name":"p","in":"query","required":false,"description":"The page, from 1 to 201: the list reads no further than its first 10,000 revisions, as `GET /api/v1/entities/{vhid}/history` does. Anything below 1 or non-numeric reads as 1; a page past 201 answers as page 202, with an empty `history` and `hasMore` false.","schema":{"default":1,"type":"integer","minimum":1,"maximum":201}}],"responses":{"200":{"description":"A page of the history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntityHistoryPage"},"examples":{"firstPage":{"summary":"The first page","description":"GET /james/history.json","value":{"vhid":"53500361835","key":"james","page":1,"hasMore":false,"history":[{"ref_id":"r1","kind":"forward","ts":"2026-09-19T12:00:00.000Z","actor":null,"summary":null,"is_bot":false,"source":"import","hidden":false,"deleted":false,"proposer":null}]}}}}}},"301":{"description":"The key is an old name: the same request under the entry’s canonical key.","headers":{"Location":{"description":"The canonical address, query kept.","schema":{"type":"string"}}}},"400":{"description":"A key and a session cookie were both sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key sent was refused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"403":{"description":"The entry’s history is hidden: not public, and how to ask for it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HistoryHidden"}}}},"404":{"description":"No public entry answers to that key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{},{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"optional","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=0, s-maxage=300, stale-while-revalidate=300","surrogate-keys":["entity:<vhid>","entity:<merged_into>"],"etag":false,"notes":"Shared caches keep it for 5 minutes, and an edit purges it at once. Browsers revalidate on every request. `Vary: Accept`; the representation is chosen by the address, never by `Accept`."},"x-cors":false}},"/oauth/token":{"post":{"operationId":"oauthToken","tags":["Connecting an app"],"summary":"Exchange a code, or refresh a connection","description":"The OAuth 2.1 token endpoint, for MCP clients a person has connected to their account. `grant_type=authorization_code` redeems the one-time code the person’s consent sent the client (it works once, for 60 seconds), with the PKCE `code_verifier` whose S256 challenge came with the authorization request, the same `redirect_uri` and `client_id`, and optionally the `resource` (the MCP address the consent was for). `grant_type=refresh_token` swaps the current refresh token for a new pair; the old pair stops working at once. A code presented a second time, or a refresh token that has been replaced, ends the connection: one copy of it was stolen. The connection acts as the person, never above the role ceiling they chose or their role of the moment, read-only if they chose that, until the 7, 30 or 90 days they chose; a refresh never extends it. Public clients only: send `client_id`, and no `Authorization` header, `client_secret` or `client_assertion` (each is refused `invalid_client`, 401).","parameters":[],"requestBody":{"description":"The token request, as a form (at most 8 KB; each field at most once).","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"]},"client_id":{"type":"string","description":"The client’s id: the URL of its Client ID Metadata Document, or the `vhdcr_…` id registration issued it."},"code":{"description":"`authorization_code`: the code (`vhc_…`).","type":"string"},"redirect_uri":{"description":"`authorization_code`: exactly the `redirect_uri` the code was sent to.","type":"string"},"code_verifier":{"description":"`authorization_code`: the PKCE verifier, 43 to 128 characters.","type":"string"},"refresh_token":{"description":"`refresh_token`: the current refresh token (`vhr_…`).","type":"string"},"resource":{"description":"The MCP address the connection is for. When sent, it must be the one the consent named (`invalid_target`).","type":"string"},"scope":{"description":"Ignored: every connection has the one scope, `mcp`.","type":"string"}},"required":["grant_type","client_id"],"additionalProperties":false},"example":{"grant_type":"authorization_code"}}}},"responses":{"200":{"description":"A new access and refresh token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthTokens"}}}},"400":{"description":"`invalid_request`: not a form, a field sent twice, or a required field missing. `unsupported_grant_type`. `invalid_grant`: the code or refresh token is unknown, expired, already used or replaced, was issued to another client or `redirect_uri`, the verifier does not match, or the person may no longer connect apps at that ceiling. `invalid_target`: the `resource` is not the one the consent named.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"examples":{"missing-fields":{"summary":"A code exchange with nothing but its grant type","description":"POST /oauth/token","value":{"error":"invalid_request","error_description":"code, redirect_uri, code_verifier and client_id are required."}},"unknown-code":{"summary":"A code the server never issued","description":"POST /oauth/token","value":{"error":"invalid_grant","error_description":"The grant is invalid, expired, already used, or was issued to another client."}},"password-grant":{"summary":"A grant type the server does not offer","description":"POST /oauth/token","value":{"error":"unsupported_grant_type","error_description":"Only authorization_code and refresh_token are supported."}}}}}},"401":{"description":"`invalid_client`. Public clients only: send `client_id`, and no `Authorization` header, `client_secret` or `client_assertion` (each is refused `invalid_client`, 401).","headers":{"WWW-Authenticate":{"description":"In the scheme the client tried.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"Connecting apps is switched off for now (`temporarily_unavailable`). (While the site is down for maintenance, the answer is the maintenance page, as HTML.)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"ignored","x-rate-limit":{"bucket":"oauth","max":60,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached, and `Pragma: no-cache`: every answer is about one connection."},"x-cors":false}},"/oauth/revoke":{"post":{"operationId":"oauthRevoke","tags":["Connecting an app"],"summary":"Disconnect","description":"Token revocation (RFC 7009). Either of a connection’s current tokens ends the whole connection: the person consented once, so there is one thing to end. A token that is unknown, malformed, replaced, already revoked or expired answers the same empty 200, as the RFC asks. A token issued to another client is refused (`invalid_client`, 401) and nothing is revoked. Public clients only: send `client_id`, and no `Authorization` header, `client_secret` or `client_assertion` (each is refused `invalid_client`, 401).","parameters":[],"requestBody":{"description":"The revocation request, as a form (at most 8 KB; each field at most once).","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"token":{"type":"string","description":"The access (`vho_…`) or refresh (`vhr_…`) token."},"client_id":{"type":"string","description":"The client’s id, as it was connected."},"token_type_hint":{"description":"Ignored: the token says which kind it is.","type":"string"}},"required":["token","client_id"],"additionalProperties":false},"example":{"token":"vho_000000000000_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","client_id":"https://client.example/oauth/metadata.json"}}}},"responses":{"200":{"description":"Done, or there was nothing to do. No body."},"400":{"description":"`invalid_request`: not a form, a field sent twice, or `token` or `client_id` missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"examples":{"no-token":{"summary":"No token sent","description":"POST /oauth/revoke","value":{"error":"invalid_request","error_description":"token and client_id are required."}}}}}},"401":{"description":"`invalid_client`. Public clients only: send `client_id`, and no `Authorization` header, `client_secret` or `client_assertion` (each is refused `invalid_client`, 401).","headers":{"WWW-Authenticate":{"description":"In the scheme the client tried.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"Connecting apps is switched off for now (`temporarily_unavailable`). (While the site is down for maintenance, the answer is the maintenance page, as HTML.)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"ignored","x-rate-limit":{"bucket":"oauth","max":60,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached, and `Pragma: no-cache`: every answer is about one connection."},"x-cors":false}},"/oauth/register":{"post":{"operationId":"oauthRegister","tags":["Connecting an app"],"summary":"Register a client","description":"Dynamic Client Registration (RFC 7591), for an MCP client without a Client ID Metadata Document (Cursor is one). It sends its `client_name` and `redirect_uris` and gets a `client_id` back; there is no secret, and nothing is checked about who made it, so when a person connects such an app the consent page marks it unverified and shows where it returns them to. A redirect URI is kept when it is `https:`, loopback `http:` (127.0.0.1, [::1] or localhost; any port then matches) or a private-use scheme with a dot (RFC 8252 §7.1, such as `com.example.app:/callback`; never one that is or starts with http, https, javascript, data, file, vbscript, about or blob), and dropped otherwise. Only `client_name`, `redirect_uris`, `token_endpoint_auth_method` (`none` or absent), `grant_types` and `response_types` are read; nothing in the request is ever fetched. A client never used for a consent is deleted 24 hours after it registered; a used one once none of its connections has been live for 90 days. Any `Authorization` header is ignored: a registration acts for no account.","parameters":[],"requestBody":{"description":"The client’s metadata, as JSON (at most 32 KB).","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_name":{"type":"string","description":"Required: 1 to 100 characters, shown to the person as the name the app gave itself."},"redirect_uris":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"string"},"description":"Where the person may be sent back to; at least one must be kept."},"token_endpoint_auth_method":{"type":"string","const":"none"},"grant_types":{"description":"When sent, must include `authorization_code`.","type":"array","items":{"type":"string"}},"response_types":{"description":"When sent, must include `code`.","type":"array","items":{"type":"string"}}},"required":["client_name","redirect_uris"],"additionalProperties":{}},"example":{"client_name":"Example MCP Client","redirect_uris":["https://client.example/oauth/callback","http://localhost:8787/callback","example://oauth/callback"],"grant_types":["authorization_code","refresh_token"],"response_types":["code"],"token_endpoint_auth_method":"none"}}}},"responses":{"201":{"description":"Registered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthRegisteredClient"},"examples":{"cursor":{"summary":"A client registering an https, a loopback and a dotless custom redirect URI (the last is dropped)","description":"POST /oauth/register","value":{"client_id":"vhdcr_AAAAAAAAAAAAAAAAAAAAAA","client_id_issued_at":1791460800,"client_name":"Example MCP Client","redirect_uris":["https://client.example/oauth/callback","http://localhost:8787/callback"],"grant_types":["authorization_code","refresh_token"],"response_types":["code"],"token_endpoint_auth_method":"none"}}}}}},"400":{"description":"`invalid_client_metadata`: not JSON, not an object, no usable `client_name`, a client secret asked for, or `grant_types`/`response_types` without the code flow. `invalid_redirect_uri`: `redirect_uris` missing, more than 10, or none of them one the server accepts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthRegisterError"},"examples":{"no-usable-redirect":{"summary":"A registration whose only redirect URI is one the server never sends a code to","description":"POST /oauth/register","value":{"error":"invalid_redirect_uri","error_description":"None of the redirect_uris is one this server accepts: https:, loopback http: (127.0.0.1, [::1] or localhost), or a private-use scheme with a dot, such as com.example.app:/callback."}},"confidential-client":{"summary":"A client that wants a secret","description":"POST /oauth/register","value":{"error":"invalid_client_metadata","error_description":"Only public clients can register: token_endpoint_auth_method must be none."}}}}}},"429":{"description":"Too many registrations from this IP address in the last hour (10), as plain text; or, while 1,000 registered clients have never been used, `temporarily_unavailable` as JSON. Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until it may succeed.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"Connecting apps is switched off for now (`temporarily_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthRegisterError"}}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"ignored","x-rate-limit":{"bucket":"oauth-register","max":10,"window_seconds":3600,"per":"IP address"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"Never cached, and `Pragma: no-cache`: every answer is about one connection."},"x-cors":false}},"/.well-known/oauth-protected-resource/mcp/account":{"get":{"operationId":"oauthProtectedResourceAccount","tags":["Connecting an app"],"summary":"Where to sign in for /mcp/account","description":"RFC 9728 protected resource metadata for the MCP address `/mcp/account`, the one that signs a person in: with no token it answers `401`. A `401` from that address names this document in its `WWW-Authenticate` header (`resource_metadata`), so an MCP client finds it by itself.","parameters":[],"responses":{"200":{"description":"The document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthProtectedResource"},"examples":{"document":{"summary":"The document for /mcp/account","description":"GET /.well-known/oauth-protected-resource/mcp/account","value":{"resource":"https://verifiedhandles.org/mcp/account","authorization_servers":["https://verifiedhandles.org"],"scopes_supported":["mcp"],"bearer_methods_supported":["header"],"resource_name":"Verified Handles","resource_documentation":"https://verifiedhandles.org/developers/mcp"}}}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"ignored","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=3600","surrogate-keys":["static:oauth-metadata"],"etag":false,"notes":"The same for everyone; it changes only with a release, whose cache ban clears it."},"x-cors":false}},"/.well-known/oauth-protected-resource/mcp":{"get":{"operationId":"oauthProtectedResourceMcp","tags":["Connecting an app"],"summary":"Where to sign in for /mcp","description":"RFC 9728 protected resource metadata for the MCP address `/mcp`, which also answers with no token at all, with the public reads. A `401` from that address names this document in its `WWW-Authenticate` header (`resource_metadata`), so an MCP client finds it by itself.","parameters":[],"responses":{"200":{"description":"The document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthProtectedResource"},"examples":{"document":{"summary":"The document for /mcp","description":"GET /.well-known/oauth-protected-resource/mcp","value":{"resource":"https://verifiedhandles.org/mcp","authorization_servers":["https://verifiedhandles.org"],"scopes_supported":["mcp"],"bearer_methods_supported":["header"],"resource_name":"Verified Handles","resource_documentation":"https://verifiedhandles.org/developers/mcp"}}}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"ignored","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=3600","surrogate-keys":["static:oauth-metadata"],"etag":false,"notes":"The same for everyone; it changes only with a release, whose cache ban clears it."},"x-cors":false}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"oauthAuthorizationServer","tags":["Connecting an app"],"summary":"The authorization server","description":"RFC 8414 authorization server metadata: where to send the person (`authorization_endpoint`), where to get and refresh tokens, and what the server supports. The authorization code flow with PKCE (S256), for public clients that identify themselves with a Client ID Metadata Document; one scope, `mcp`. What a connection may do is the person’s choice when they connect it (a role ceiling, read only, 7, 30 or 90 days), not a scope.","parameters":[],"responses":{"200":{"description":"The document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthAuthorizationServer"},"examples":{"document":{"summary":"The document","description":"GET /.well-known/oauth-authorization-server","value":{"issuer":"https://verifiedhandles.org","authorization_endpoint":"https://verifiedhandles.org/management-console/oauth/authorize","token_endpoint":"https://verifiedhandles.org/oauth/token","revocation_endpoint":"https://verifiedhandles.org/oauth/revoke","registration_endpoint":"https://verifiedhandles.org/oauth/register","response_types_supported":["code"],"grant_types_supported":["authorization_code","refresh_token"],"code_challenge_methods_supported":["S256"],"token_endpoint_auth_methods_supported":["none"],"revocation_endpoint_auth_methods_supported":["none"],"scopes_supported":["mcp"],"client_id_metadata_document_supported":true,"authorization_response_iss_parameter_supported":true,"service_documentation":"https://verifiedhandles.org/developers/mcp"}}}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}}},"security":[],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"ignored","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=3600","surrogate-keys":["static:oauth-metadata"],"etag":false,"notes":"The same for everyone; it changes only with a release, whose cache ban clears it."},"x-cors":false}},"/{key}.json":{"get":{"operationId":"getEntityJson","tags":["Entries"],"summary":"Get an entry, at its page’s address","description":"The entry as data, at its page’s address plus `.json`: the same body as `GET /api/v1/entities/{vhid}`. An old name or a merged entry’s key answers with the entry it now leads to, without a redirect; `url` is that entry’s canonical page, and its own `.json` is that plus `.json`. A key nothing answers to is a JSON 404 and is never redirected to a guess, as the page would be.\n\nSame-origin only: it sends no CORS headers (use `/api/v1` from a browser on another site). A key is not needed. One that is sent is checked: a bad one is refused (400 or 401) before the route runs, and a good one makes the answer private (`Cache-Control: no-store`, `Vary: Authorization`).","parameters":[{"name":"key","in":"path","required":true,"description":"The entry’s VHID (11 digits), its short name, or one of its old names. Short names are matched exactly. Written before `.json`, so `/james.json` or `/53500361835.json`.","schema":{"type":"string","minLength":1,"maxLength":255},"example":"james"}],"responses":{"200":{"description":"The entry.","headers":{"ETag":{"description":"The version of this representation.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Entity"},"examples":{"byShort":{"summary":"An entry by its short name","description":"GET /james.json","value":{"vhid":"53500361835","short":"james","title":"James Haworth","subtitle":null,"entity_type":"person","entity_kind":"person","verified":true,"description":null,"description_source":null,"description_license":null,"url":"https://verifiedhandles.org/james","image":"/img/w/480q75/3","image_rights":{"source":"upload","license":null,"license_name":null,"license_url":null,"freely_licensed":null,"credit":null,"source_page":null,"details_url":"https://verifiedhandles.org/james/image"},"images":[{"url":"/img/w/480q75/3","main":true,"image_rights":{"source":"upload","license":null,"license_name":null,"license_url":null,"freely_licensed":null,"credit":null,"source_page":null,"details_url":"https://verifiedhandles.org/james/image"},"link":null}],"handles":[{"platform":"twitter","name":"X (Twitter)","category":"social","value":"james","display":null,"url":"https://x.com/james","verified":false},{"platform":"linkedin","name":"LinkedIn","category":"social","value":"james-haworth","display":null,"url":"https://www.linkedin.com/in/james-haworth","verified":false},{"platform":"instagram","name":"Instagram","category":"social","value":"jameshaworth","display":null,"url":"https://instagram.com/jameshaworth","verified":false},{"platform":"email","name":"Email","category":"messaging","value":null,"display":null,"url":null,"verified":false}]}}}}}},"304":{"description":"Unchanged since the ETag sent in `If-None-Match`. No body."},"400":{"description":"A key and a session cookie were both sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key sent was refused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"404":{"description":"No public entry answers to that key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundKey"},"examples":{"notFound":{"summary":"A key nothing answers to","description":"GET /no-such-entry.json","value":{"error":"not_found","key":"no-such-entry"}}}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{},{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"optional","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=60, s-maxage=2592000, stale-while-revalidate=86400, stale-if-error=604800","surrogate-keys":["entity:<vhid>","kind:<entity_kind>","type:<entity_type>"],"etag":true,"notes":"Shared caches may keep it for 30 days; an edit to the entry purges it at once. Browsers keep it for a minute. A strong ETag answers `If-None-Match` with a 304. `Vary: Accept`, as every page with a twin sends: the representation is chosen by the address, never by `Accept`."},"x-cors":false}},"/get_data":{"get":{"operationId":"typeahead","tags":["Search"],"summary":"Suggest entries as a name is typed","description":"What the search box suggests: up to 10 entries whose name starts with, contains or nearly matches the text, best first. With no text, the trending entries. A blocked word answers `[\"block\"]`. Its shape is the old site’s, kept for the search box; for anything else `GET /api/v1/search` is the better read, with paging and the entries’ details.\n\nSame-origin only: it sends no CORS headers. A key is not needed. One that is sent is checked: a bad one is refused (400 or 401) before the route runs, and a good one makes the answer private (`Cache-Control: no-store`, `Vary: Authorization`).","parameters":[{"name":"search_query","in":"query","required":false,"description":"The text typed so far; cut to 100 characters. Empty: the trending entries.","schema":{"type":"string","maxLength":100},"example":"jam"}],"responses":{"200":{"description":"The suggestions, or `[\"block\"]`.","content":{"application/json":{"schema":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/TypeaheadRow"}},{"type":"array","prefixItems":[{"type":"string","const":"block"}],"items":false,"minItems":1,"maxItems":1}]},"examples":{"jam":{"summary":"Part of a name","description":"GET /get_data?search_query=jam","value":[{"VHID":"53500361835","Title":"James Haworth","Subtitle":null,"short":"james","V":"1","S":"0","T":""}]}}}}},"400":{"description":"A key and a session cookie were both sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"The key sent was refused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"429":{"description":"Too many requests from this IP address in the last minute (at a route that takes a key, an admin key’s are counted per key instead, at the limits set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{},{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":true,"x-auth":"optional","x-rate-limit":{"bucket":"json","max":120,"window_seconds":60,"per":"IP address"},"x-cache":{"cache-control":"public, max-age=0, s-maxage=60, stale-while-revalidate=60","surrogate-keys":["listing"],"etag":false,"notes":"Shared caches keep it for a minute (with no text: `public, max-age=0, s-maxage=60`), and any change to an entry’s listing purges it. A blocked word is never cached."},"x-cors":false}},"/{key}/report":{"post":{"operationId":"reportEntry","tags":["Reporting an entry"],"summary":"Report an entry","description":"Reports a problem with an entry to the site’s reviewers. It is the report page’s own form, so it answers as the form does: a 303 back to the page, `?sent=1` when the report was taken, `?error=<what>` when it was not (`reason`: no such reason, or an image report on an entry with no image; `note`: an image report needs a note; `blocked`: the note has a word the site does not take). An image report names the image (`media`), or the lead image is meant.","parameters":[{"name":"key","in":"path","required":true,"description":"The entry’s VHID or short name.","schema":{"type":"string","minLength":1,"maxLength":255},"example":"james"}],"requestBody":{"description":"The report, as a form.","required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"reason":{"type":"string","enum":["impersonation","incorrect","outdated","abuse","image","image_link","other"],"description":"`impersonation`: a handle is not the real account. `incorrect`, `outdated`, `abuse`, `other`. `image`: the image is used without permission, credited or licensed wrongly, or shows me. `image_link`: an image’s link goes somewhere wrong."},"note":{"description":"What is wrong, in your words. Needed for `image`. Cut to 1,000 characters.","type":"string","maxLength":1000},"media":{"description":"For `image` or `image_link`: which of the entry’s images, by media id.","type":"string"}},"required":["reason"],"additionalProperties":false},"example":{"reason":"outdated","note":"The Instagram account was renamed."}}}},"responses":{"301":{"description":"An old name: the same request belongs at the entry’s current key.","headers":{"Location":{"description":"The report page at the current key.","schema":{"type":"string"}}}},"303":{"description":"Back to the report page: `?sent=1` when taken, `?error=<what>` when not.","headers":{"Location":{"description":"The report page, with the outcome in its query.","schema":{"type":"string"}}}},"400":{"description":"Or the request sent a key and a session cookie together (`bearer_with_cookie`): send one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRefusal"}}}},"401":{"description":"No key was sent (`auth_required`), or the key was refused: unknown, revoked or expired, its holder may no longer use keys, or this instance takes none. Carries `WWW-Authenticate: Bearer`. After 60 refused keys from one IP address in a minute, every key from there is answered `429` (text, with `Retry-After`) until the minute is up.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AuthRequired"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"403":{"description":"A read-only key tried to change something (`read_only_key`), or the account is blocked (`forbidden`).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/Forbidden"},{"$ref":"#/components/schemas/ApiKeyRefusal"}]}}}},"404":{"description":"No public entry answers to that key. The body is a page.","content":{"text/html":{}}},"429":{"description":"Too many changes in the last minute: counted per IP address, per account and per key (an admin key’s per key alone, at the limit set on Console → Rate limits). Wait the number of seconds `Retry-After` gives.","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"string"}}},"content":{"text/plain":{"schema":{"type":"string","const":"Too Many Requests"}}}},"503":{"description":"The site is down for maintenance. The body is the maintenance page, as HTML. Try again later: `Retry-After` gives the seconds.","content":{"text/html":{}}}},"security":[{"bearerAuth":[]}],"x-audience":"public","x-capability":null,"x-read-only-key":false,"x-auth":"required","x-rate-limit":{"bucket":"mutation"},"x-cache":{"cache-control":"no-store","etag":false,"notes":"A redirect, never cached."},"x-cors":false}}},"x-mcp-tools":{"endpoint":"https://verifiedhandles.org/mcp","transport":"streamable-http","authentication":"An API key, as `Authorization: Bearer`: the tools listed are the ones the key may use. With no `Authorization` header at all, only the tools marked `keyless: true`, as a signed-out visitor.","docs":"https://verifiedhandles.org/developers/mcp","contract":"A tool that changes something (`write: true`) writes nothing unless called with `dry_run: false`: by default it answers what it would change and the exact request it would send. A tool with `confirm: true` also needs `confirm` set to the exact target its dry run names. A read-only key is shown no write tool. Every call goes through the site’s own routes as the key’s account, and every change is audited with the key and the tool.","tools":[{"name":"get_entry","title":"Get an entry","description":"One entry, by VHID, short name or Wikidata QID: its public record (title, description, handles, image, URL) and, as `editing`, what the editor would load for this key — the current revision, the editable fields, every handle with who manages it (the Wikidata sync or the site), which fields the sync manages, and whether this key may edit it live. Without an API key, the public record alone.","write":false,"confirm":false,"capabilities":[],"role":null,"keyless":true,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"include_platforms":{"default":false,"description":"Also list every platform a handle can be on (id, name, URL prefix). Needs an API key.","type":"boolean"}},"required":["entry"]},"output":{"type":"object","properties":{"vhid":{"type":"string","pattern":"^\\d{11}$","description":"The entry’s VHID: 11 digits, permanent."},"entry":{"anyOf":[{"type":"object","properties":{"vhid":{"type":"string","pattern":"^\\d{11}$","description":"The entry’s VHID: 11 digits, permanent."},"short":{"description":"The entry’s short name (`/<short>`), if it has one.","type":["string","null"]},"title":{"type":"string"},"subtitle":{"type":["string","null"]},"entity_type":{"description":"What it is, from its Wikidata item: `person`, `organization` or `brand`; null when unknown. `entity_kind` says more.","type":["string","null"]},"entity_kind":{"anyOf":[{"type":"string","enum":["person","musical_group","sports_organization","broadcaster","publisher","educational_institution","research_organization","religious_organization","museum","financial_institution","chain","nonprofit","company","organization","show","website","software","brand"]},{"type":"null"}],"description":"The finer kind of thing it is, from its Wikidata item’s classes; null when unknown (an organisation or a brand not yet read since kinds were added reads null until the sync next reads its item). Each kind belongs to one `entity_type`, and `person`, `organization` and `brand` are also the kinds of an entry of that type with no finer one. The kinds, with their type: `person` (person: person), `musical_group` (organization: musical group), `sports_organization` (organization: sports organisation), `broadcaster` (organization: broadcaster), `publisher` (organization: publisher), `educational_institution` (organization: educational institution), `research_organization` (organization: research organisation), `religious_organization` (organization: religious organisation), `museum` (organization: museum), `financial_institution` (organization: financial institution), `chain` (organization: retail or hospitality chain), `nonprofit` (organization: charity or non-profit), `company` (organization: company), `organization` (organization: organisation), `show` (brand: show), `website` (brand: website or app), `software` (brand: software), `brand` (brand: brand). New kinds may be added; treat an unknown value as its `entity_type`."},"verified":{"type":"boolean","description":"Verified Handles has verified the entry."},"description":{"description":"The description as sanitised HTML, links to other entries named as they are today.","type":["string","null"]},"description_source":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","description":"The source’s kind (`wikipedia`)."},"title":{"type":"string","description":"The page’s title, underscored (`Draft:` first for a draft)."},"url":{"type":"string","description":"The page."},"adapted":{"description":"Present and true when the text has been edited on the site since.","type":"boolean"},"revision":{"description":"The page’s revision the text was taken from: its permalink is `https://en.wikipedia.org/w/index.php?title=<title>&oldid=<revision>`. Absent on a credit from before revisions were kept.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"draft":{"description":"Present and true when the page is an English Wikipedia draft (the Draft namespace), not yet an article.","type":"boolean","const":true}},"required":["kind","title","url"],"additionalProperties":{},"description":"The English Wikipedia page an entry’s description was taken from: an article, or a draft (`draft: true`)."},{"type":"null"}]},"description_license":{"anyOf":[{"type":"string","const":"CC-BY-SA-4.0"},{"type":"null"}],"description":"The description’s licence, as an SPDX id: `CC-BY-SA-4.0` when it was taken from Wikipedia (`description_source` is set), which means crediting the article and sharing alike. Null otherwise: the description is CC0 like the rest of the text."},"url":{"type":"string","description":"The entry’s canonical page."},"image":{"description":"The lead image (the same as `images[0].url`), or null.","type":["string","null"]},"image_rights":{"anyOf":[{"type":"object","properties":{"source":{"type":"string","enum":["wikimedia_commons","upload"],"description":"Where the image came from."},"license":{"anyOf":[{"type":"string","enum":["cc0-1.0","public-domain","cc-by-4.0","cc-by-sa-4.0","supplied","permission","none"]},{"type":"null"}],"description":"An upload’s licence, by id. Null for a Commons image (see `license_name`) or when none was recorded."},"license_name":{"description":"The licence’s name, e.g. \"CC BY-SA 4.0\". Null until it is known.","type":["string","null"]},"license_url":{"description":"The licence’s text.","type":["string","null"]},"freely_licensed":{"description":"May be reused under a free licence. Null when no licence is recorded.","type":["boolean","null"]},"credit":{"description":"The credit line to show when reusing the image.","type":["string","null"]},"source_page":{"description":"The image’s Wikimedia Commons file page, which states its licence and author.","type":["string","null"]},"details_url":{"type":"string","description":"The entry’s \"About this image\" page."}},"required":["source","license","license_name","license_url","freely_licensed","credit","source_page","details_url"],"additionalProperties":{},"description":"Who owns an image and how it may be reused. An entry’s text is CC0 (bar a description taken from Wikipedia); its images are licensed separately, by their owners."},{"type":"null"}],"description":"The lead image’s rights, or null when there is no image."},"images":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","description":"The image, as a path on this site (`/img/w/480q75/<id>`)."},"main":{"type":"boolean","description":"True for the lead image, false for the gallery images after it."},"image_rights":{"type":"object","properties":{"source":{"type":"string","enum":["wikimedia_commons","upload"],"description":"Where the image came from."},"license":{"anyOf":[{"type":"string","enum":["cc0-1.0","public-domain","cc-by-4.0","cc-by-sa-4.0","supplied","permission","none"]},{"type":"null"}],"description":"An upload’s licence, by id. Null for a Commons image (see `license_name`) or when none was recorded."},"license_name":{"description":"The licence’s name, e.g. \"CC BY-SA 4.0\". Null until it is known.","type":["string","null"]},"license_url":{"description":"The licence’s text.","type":["string","null"]},"freely_licensed":{"description":"May be reused under a free licence. Null when no licence is recorded.","type":["boolean","null"]},"credit":{"description":"The credit line to show when reusing the image.","type":["string","null"]},"source_page":{"description":"The image’s Wikimedia Commons file page, which states its licence and author.","type":["string","null"]},"details_url":{"type":"string","description":"The entry’s \"About this image\" page."}},"required":["source","license","license_name","license_url","freely_licensed","credit","source_page","details_url"],"additionalProperties":{},"description":"Who owns an image and how it may be reused. An entry’s text is CC0 (bar a description taken from Wikipedia); its images are licensed separately, by their owners."},"link":{"description":"Where the image links on the entry page instead of opening the full-screen viewer, or null.","type":["string","null"]}},"required":["url","main","image_rights","link"],"additionalProperties":{},"description":"One image an entry shows."},"description":"Every image the entry shows, the lead first. Empty when it shows none."},"handles":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string","description":"The platform’s id (`twitter`, `instagram`, `wikidata`, …)."},"name":{"type":"string","description":"The platform’s display name."},"category":{"type":"string","enum":["social","messaging","website","identifier"],"description":"How the entry page groups it."},"value":{"description":"The handle as stored (a username, an id, a URL). Null for an email address, which is never published as data.","type":["string","null"]},"display":{"description":"The label the entry page shows instead of the value, if an editor set one. Null for email.","type":["string","null"]},"url":{"description":"The profile’s address, built from the platform’s URL pattern. Null for email.","type":["string","null"]},"verified":{"type":"boolean","description":"Verified Handles has checked that this account belongs to the entry."},"checked":{"description":"What Verified Handles’ own handle checker found when it last looked at this account, from the platform’s official API: only on YouTube (channel and handle), Deezer and GitHub, and only for a look at most 7 days old. Absent otherwise. The entry page shows it only for `dead` (“Not found when checked …”) and `renamed`.","type":"object","properties":{"verdict":{"type":"string","enum":["live","dead","renamed","unknown"],"description":"`live`: the profile was there. `dead`: the platform did not find it. `renamed`: it answers under another name. `unknown`: the platform gave no answer a rule could read."},"at":{"type":"string","description":"When it looked (ISO 8601, UTC)."},"now":{"description":"For `renamed`, the name it answers to now, when the platform gave one. A YouTube handle’s is lowercase.","type":"string"}},"required":["verdict","at"],"additionalProperties":{}}},"required":["platform","name","category","value","display","url","verified"],"additionalProperties":{},"description":"One account or identifier of an entry, in the order the entry page shows them. A Wikidata item linked to the entry comes last, as platform `wikidata`."}}},"required":["vhid","short","title","subtitle","entity_type","entity_kind","verified","description","description_source","description_license","url","image","image_rights","images","handles"],"additionalProperties":{},"description":"An entry: the same data its page shows, from the same code. Email addresses are masked. The text is CC0, except a description taken from Wikipedia (`description_license`)."},{"type":"null"}],"description":"The entry’s public record, as GET /api/v1/entities/{vhid} answers it. Null for a VHID that is not public (a deleted entry): `editing` says more."},"editing":{"description":"With an API key: what the editor loads for this key (GET /api/editor/{vhid}): `baseRevisionId`, `capabilities` (whether it may edit live or propose), `editable`, `handles`, `provenance` (which fields the Wikidata sync manages), `qid`, and `platforms` with include_platforms. Where the key may not open the editor, the route’s `status` and `error`. Absent with no key.","type":"object","properties":{},"additionalProperties":{}}},"required":["vhid","entry"],"additionalProperties":{},"description":"One entry: its public record and, with an API key, what the editor would load for it."}},{"name":"search_entries","title":"Search entries","description":"Search the entries as the site search does: words match titles, subtitles and descriptions; a Wikidata QID finds the entries linked to it; a VHID finds that entry and the entries that link to it.","write":false,"confirm":false,"capabilities":[],"role":null,"keyless":true,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"query":{"type":"string","minLength":1,"maxLength":100},"limit":{"default":20,"type":"integer","minimum":1,"maximum":50},"offset":{"default":0,"type":"integer","minimum":0,"maximum":10000}},"required":["query"]},"output":{"type":"object","properties":{"query":{"type":"string","description":"The query as it was read: trimmed and cut to 100 characters."},"total":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many entries matched in all."},"limit":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"The page size used: the `limit` asked for, clamped to the endpoint’s range."},"offset":{"type":"integer","minimum":0,"maximum":10000,"description":"The offset used: the `offset` asked for, clamped to 0–10,000."},"next_offset":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}],"description":"The `offset` of the next page, or null when this is the last one. Follow it rather than computing it."},"results":{"type":"array","items":{"type":"object","properties":{"vhid":{"type":"string","pattern":"^\\d{11}$","description":"The entry’s VHID: 11 digits, permanent."},"short":{"description":"The entry’s short name (`/<short>`), if it has one.","type":["string","null"]},"title":{"type":"string"},"subtitle":{"type":["string","null"]},"verified":{"type":"boolean"},"snippet":{"description":"Plain text from the description around the words that matched, or null when the match was not in it.","type":["string","null"]},"highlighted":{"type":"boolean","description":"`snippet` is a real excerpt (true), or there is none (false)."},"url":{"type":"string","description":"The entry’s page."}},"required":["vhid","short","title","subtitle","verified","snippet","highlighted","url"],"additionalProperties":{},"description":"One entry that matched."},"description":"Best match first."}},"required":["query","total","results"],"additionalProperties":{}}},{"name":"find_by_handle","title":"Find entries by handle","description":"Which public entries list a handle: “is x.com/someone an official account, and whose?”. Give a profile’s address as url (https://x.com/someone), or platform (its catalog id: twitter, instagram, youtube, …) and value (someone or @someone). Answers each entry’s VHID, title and URL, the handle as the entry lists it, and whether that handle is verified; an empty list when no entry lists it. Case is ignored where the platform ignores it. Email addresses are not looked up.","write":false,"confirm":false,"capabilities":[],"role":null,"keyless":true,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"url":{"description":"A profile’s address, such as https://x.com/someone. Instead of platform and value.","type":"string","minLength":1,"maxLength":1000},"platform":{"description":"The platform’s catalog id, such as twitter. With value.","type":"string","minLength":1,"maxLength":100},"value":{"description":"The handle on that platform, such as someone or @someone.","type":"string","minLength":1,"maxLength":1000}}},"output":{"type":"object","properties":{"platform":{"type":"string","description":"The platform looked on: the one asked, or the one whose address `url` is."},"value":{"type":"string","description":"The handle looked up, as the site stores one: from `url`, what follows the platform’s address."},"results":{"type":"array","items":{"type":"object","properties":{"vhid":{"type":"string","description":"The entry’s VHID."},"title":{"type":"string","description":"The entry’s title."},"url":{"type":"string","description":"The entry’s page."},"value":{"type":"string","description":"The handle as the entry lists it: on a platform that ignores case, its case may differ from the value asked."},"verified":{"type":"boolean","description":"Whether the handle itself is verified on this entry (not the entry’s own badge)."}},"required":["vhid","title","url","value","verified"],"additionalProperties":{},"description":"An entry that lists the handle."},"description":"Verified handles first, then by VHID; at most 50. Empty when no public entry lists it."}},"required":["platform","value","results"],"additionalProperties":{},"description":"The public entries that list a handle."}},{"name":"get_entry_history","title":"Entry history","description":"An entry’s public edit history, newest first (`next_offset` pages on). A merged entry’s key gives its own history.","write":false,"confirm":false,"capabilities":[],"role":null,"keyless":true,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"limit":{"default":50,"type":"integer","minimum":1,"maximum":200},"offset":{"default":0,"type":"integer","minimum":0,"maximum":10000}},"required":["entry"]},"output":{"type":"object","properties":{"vhid":{"type":"string","pattern":"^\\d{11}$","description":"The entry’s VHID: 11 digits, permanent."},"merged_into":{"description":"Present when the entry was merged into another: that entry’s VHID. The history is still the merged entry’s own.","type":"string","pattern":"^\\d{11}$"},"limit":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"The page size used: the `limit` asked for, clamped to the endpoint’s range."},"offset":{"type":"integer","minimum":0,"maximum":10000,"description":"The offset used: the `offset` asked for, clamped to 0–10,000."},"next_offset":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}],"description":"The `offset` of the next page, or null when this is the last one. Follow it rather than computing it."},"history":{"type":"array","items":{"type":"object","properties":{"ref_id":{"type":"string","pattern":"^[ar]\\d+$","description":"The revision’s id: `a<n>` for the history kept from the old wiki, `r<n>` for an edit made since."},"kind":{"type":"string","enum":["archive","forward"],"description":"`archive`: from the old wiki. `forward`: made on Verified Handles."},"ts":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When it was made."},"actor":{"description":"Who made it, as they chose to be named in public; null when they keep their name private.","type":["string","null"]},"summary":{"description":"The edit summary.","type":["string","null"]},"is_bot":{"description":"Made by an automated process (the Wikidata sync, an import).","type":["boolean","null"]},"source":{"type":"string","enum":["import","mediawiki","sync","admin","trusted","submission","claim"],"description":"How the edit was made."},"hidden":{"type":"boolean","const":false,"description":"Always false here: a hidden revision is never in the public history."},"deleted":{"type":"boolean","const":false,"description":"Always false here: a deleted revision is never in the public history."},"proposer":{"description":"For an approved proposal, who proposed it, as they chose to be named; null otherwise.","type":["string","null"]}},"required":["ref_id","kind","ts","actor","summary","is_bot","source","hidden","deleted","proposer"],"additionalProperties":{},"description":"One revision in an entry’s public history."},"description":"Newest first."}},"required":["vhid","limit","offset","next_offset","history"],"additionalProperties":{},"description":"A page of an entry’s public revision history, newest first."}},{"name":"get_entries","title":"Get many entries","description":"Up to 50 entries’ public records in one call, by VHID or short name (not a Wikidata QID: get_entry or search_entries finds an entry by its item), each answered as get_entry answers it signed out — the entry, or not_found — in the order given. A name given twice is answered once.","write":false,"confirm":false,"capabilities":[],"role":null,"keyless":true,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"entries":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":255,"allOf":[{"pattern":"^(?!\\.{1,2}$)"},{"pattern":"^[^,]*$"}]},"description":"The entries: each a VHID (11 digits) or a short name."}},"required":["entries"]},"output":{"type":"object","properties":{"results":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"key":{"type":"string","description":"The key as asked for, trimmed."},"status":{"type":"string","const":"ok"},"entity":{"type":"object","properties":{"vhid":{"type":"string","pattern":"^\\d{11}$","description":"The entry’s VHID: 11 digits, permanent."},"short":{"description":"The entry’s short name (`/<short>`), if it has one.","type":["string","null"]},"title":{"type":"string"},"subtitle":{"type":["string","null"]},"entity_type":{"description":"What it is, from its Wikidata item: `person`, `organization` or `brand`; null when unknown. `entity_kind` says more.","type":["string","null"]},"entity_kind":{"anyOf":[{"type":"string","enum":["person","musical_group","sports_organization","broadcaster","publisher","educational_institution","research_organization","religious_organization","museum","financial_institution","chain","nonprofit","company","organization","show","website","software","brand"]},{"type":"null"}],"description":"The finer kind of thing it is, from its Wikidata item’s classes; null when unknown (an organisation or a brand not yet read since kinds were added reads null until the sync next reads its item). Each kind belongs to one `entity_type`, and `person`, `organization` and `brand` are also the kinds of an entry of that type with no finer one. The kinds, with their type: `person` (person: person), `musical_group` (organization: musical group), `sports_organization` (organization: sports organisation), `broadcaster` (organization: broadcaster), `publisher` (organization: publisher), `educational_institution` (organization: educational institution), `research_organization` (organization: research organisation), `religious_organization` (organization: religious organisation), `museum` (organization: museum), `financial_institution` (organization: financial institution), `chain` (organization: retail or hospitality chain), `nonprofit` (organization: charity or non-profit), `company` (organization: company), `organization` (organization: organisation), `show` (brand: show), `website` (brand: website or app), `software` (brand: software), `brand` (brand: brand). New kinds may be added; treat an unknown value as its `entity_type`."},"verified":{"type":"boolean","description":"Verified Handles has verified the entry."},"description":{"description":"The description as sanitised HTML, links to other entries named as they are today.","type":["string","null"]},"description_source":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","description":"The source’s kind (`wikipedia`)."},"title":{"type":"string","description":"The page’s title, underscored (`Draft:` first for a draft)."},"url":{"type":"string","description":"The page."},"adapted":{"description":"Present and true when the text has been edited on the site since.","type":"boolean"},"revision":{"description":"The page’s revision the text was taken from: its permalink is `https://en.wikipedia.org/w/index.php?title=<title>&oldid=<revision>`. Absent on a credit from before revisions were kept.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"draft":{"description":"Present and true when the page is an English Wikipedia draft (the Draft namespace), not yet an article.","type":"boolean","const":true}},"required":["kind","title","url"],"additionalProperties":{},"description":"The English Wikipedia page an entry’s description was taken from: an article, or a draft (`draft: true`)."},{"type":"null"}]},"description_license":{"anyOf":[{"type":"string","const":"CC-BY-SA-4.0"},{"type":"null"}],"description":"The description’s licence, as an SPDX id: `CC-BY-SA-4.0` when it was taken from Wikipedia (`description_source` is set), which means crediting the article and sharing alike. Null otherwise: the description is CC0 like the rest of the text."},"url":{"type":"string","description":"The entry’s canonical page."},"image":{"description":"The lead image (the same as `images[0].url`), or null.","type":["string","null"]},"image_rights":{"anyOf":[{"type":"object","properties":{"source":{"type":"string","enum":["wikimedia_commons","upload"],"description":"Where the image came from."},"license":{"anyOf":[{"type":"string","enum":["cc0-1.0","public-domain","cc-by-4.0","cc-by-sa-4.0","supplied","permission","none"]},{"type":"null"}],"description":"An upload’s licence, by id. Null for a Commons image (see `license_name`) or when none was recorded."},"license_name":{"description":"The licence’s name, e.g. \"CC BY-SA 4.0\". Null until it is known.","type":["string","null"]},"license_url":{"description":"The licence’s text.","type":["string","null"]},"freely_licensed":{"description":"May be reused under a free licence. Null when no licence is recorded.","type":["boolean","null"]},"credit":{"description":"The credit line to show when reusing the image.","type":["string","null"]},"source_page":{"description":"The image’s Wikimedia Commons file page, which states its licence and author.","type":["string","null"]},"details_url":{"type":"string","description":"The entry’s \"About this image\" page."}},"required":["source","license","license_name","license_url","freely_licensed","credit","source_page","details_url"],"additionalProperties":{},"description":"Who owns an image and how it may be reused. An entry’s text is CC0 (bar a description taken from Wikipedia); its images are licensed separately, by their owners."},{"type":"null"}],"description":"The lead image’s rights, or null when there is no image."},"images":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","description":"The image, as a path on this site (`/img/w/480q75/<id>`)."},"main":{"type":"boolean","description":"True for the lead image, false for the gallery images after it."},"image_rights":{"type":"object","properties":{"source":{"type":"string","enum":["wikimedia_commons","upload"],"description":"Where the image came from."},"license":{"anyOf":[{"type":"string","enum":["cc0-1.0","public-domain","cc-by-4.0","cc-by-sa-4.0","supplied","permission","none"]},{"type":"null"}],"description":"An upload’s licence, by id. Null for a Commons image (see `license_name`) or when none was recorded."},"license_name":{"description":"The licence’s name, e.g. \"CC BY-SA 4.0\". Null until it is known.","type":["string","null"]},"license_url":{"description":"The licence’s text.","type":["string","null"]},"freely_licensed":{"description":"May be reused under a free licence. Null when no licence is recorded.","type":["boolean","null"]},"credit":{"description":"The credit line to show when reusing the image.","type":["string","null"]},"source_page":{"description":"The image’s Wikimedia Commons file page, which states its licence and author.","type":["string","null"]},"details_url":{"type":"string","description":"The entry’s \"About this image\" page."}},"required":["source","license","license_name","license_url","freely_licensed","credit","source_page","details_url"],"additionalProperties":{},"description":"Who owns an image and how it may be reused. An entry’s text is CC0 (bar a description taken from Wikipedia); its images are licensed separately, by their owners."},"link":{"description":"Where the image links on the entry page instead of opening the full-screen viewer, or null.","type":["string","null"]}},"required":["url","main","image_rights","link"],"additionalProperties":{},"description":"One image an entry shows."},"description":"Every image the entry shows, the lead first. Empty when it shows none."},"handles":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string","description":"The platform’s id (`twitter`, `instagram`, `wikidata`, …)."},"name":{"type":"string","description":"The platform’s display name."},"category":{"type":"string","enum":["social","messaging","website","identifier"],"description":"How the entry page groups it."},"value":{"description":"The handle as stored (a username, an id, a URL). Null for an email address, which is never published as data.","type":["string","null"]},"display":{"description":"The label the entry page shows instead of the value, if an editor set one. Null for email.","type":["string","null"]},"url":{"description":"The profile’s address, built from the platform’s URL pattern. Null for email.","type":["string","null"]},"verified":{"type":"boolean","description":"Verified Handles has checked that this account belongs to the entry."},"checked":{"description":"What Verified Handles’ own handle checker found when it last looked at this account, from the platform’s official API: only on YouTube (channel and handle), Deezer and GitHub, and only for a look at most 7 days old. Absent otherwise. The entry page shows it only for `dead` (“Not found when checked …”) and `renamed`.","type":"object","properties":{"verdict":{"type":"string","enum":["live","dead","renamed","unknown"],"description":"`live`: the profile was there. `dead`: the platform did not find it. `renamed`: it answers under another name. `unknown`: the platform gave no answer a rule could read."},"at":{"type":"string","description":"When it looked (ISO 8601, UTC)."},"now":{"description":"For `renamed`, the name it answers to now, when the platform gave one. A YouTube handle’s is lowercase.","type":"string"}},"required":["verdict","at"],"additionalProperties":{}}},"required":["platform","name","category","value","display","url","verified"],"additionalProperties":{},"description":"One account or identifier of an entry, in the order the entry page shows them. A Wikidata item linked to the entry comes last, as platform `wikidata`."}}},"required":["vhid","short","title","subtitle","entity_type","entity_kind","verified","description","description_source","description_license","url","image","image_rights","images","handles"],"additionalProperties":{},"description":"Exactly what `GET /api/v1/entities/{key}` answers for this key."}},"required":["key","status","entity"],"additionalProperties":{}},{"type":"object","properties":{"key":{"type":"string","description":"The key as asked for, trimmed."},"status":{"type":"string","const":"not_found","description":"No public entry answers to the key: where the single read answers 404."}},"required":["key","status"],"additionalProperties":{}}],"description":"What one requested key answers."},"description":"One element per key asked for, in the order asked, a repeated key once (where it first appears)."}},"required":["results"],"additionalProperties":{},"description":"Many entries at once: each key’s answer, in request order."}},{"name":"search_commons_images","title":"Search Wikimedia Commons","description":"Search Wikimedia Commons for an image to put on an entry, as the editor’s image picker does. An exact file name finds that file first, even one uploaded minutes ago. Each hit has a `token`: pass it to set_entry_image as `commons_token`.","write":false,"confirm":false,"capabilities":[],"role":null,"keyless":false,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"query":{"type":"string","minLength":2,"maxLength":200}},"required":["query"]},"output":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"filename":{"type":"string"},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"mime":{"type":["string","null"]},"token":{"type":"string","description":"The file, signed for 30 minutes: what `POST /api/media/commons` and the preview take."},"commonsPage":{"type":"string","description":"The file’s page on Commons, with its licence and author."}},"required":["filename","width","height","mime","token","commonsPage"],"additionalProperties":{},"description":"A Wikimedia Commons file a search found."}}},"required":["results"],"additionalProperties":{}}},{"name":"update_entry_fields","title":"Edit an entry’s text","description":"Change an entry’s title, subtitle, description or short name, or remove its “from Wikipedia” credit or name the Wikipedia page it came from — a live edit, as the editor saves one: a new revision, audited. Give only what changes; null clears a field. The description is HTML (sanitised on save). A field you change here is no longer managed by the Wikidata sync; to hand one back, so the next sync refreshes it from the item, use set_wikidata_ownership (relinquish_fields). Dry run by default. To ask a reviewer instead, use propose_entry_change.","write":true,"confirm":false,"capabilities":[],"role":null,"keyless":false,"liveEdit":true,"linkImages":false,"input":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"title":{"type":"string","minLength":1,"maxLength":500},"subtitle":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"description":{"anyOf":[{"type":"string","maxLength":65536},{"type":"null"}]},"short":{"description":"The short name in the entry’s URL; null removes it (the VHID URL stays).","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"remove_wikipedia_credit":{"description":"true: remove the “from Wikipedia” credit from the description.","type":"boolean"},"credit_wikipedia_page":{"description":"Credit the description “from Wikipedia”: an English Wikipedia article or draft, by title (\"Draft:Dominic McLaughlin\") or address. The server reads the page (redirects followed) and refuses it unless at least half of the description’s three-word phrases are the page’s lead (credit_text_differs, with the share).","type":"string","minLength":1,"maxLength":2000},"credit_let_wikidata_manage":{"description":"With a draft named in credit_wikipedia_page: false keeps the description site-authored. By default a draft’s credit hands the description to the Wikidata sync, which leaves it alone until the draft is an article linked from the entry’s Wikidata item.","type":"boolean"},"summary":{"description":"Why you made this edit, in a few plain words; shown in the entry’s public history. At most 200 characters. Never an internal tracking ID (a backlog or ticket number); a VHID is fine.","type":"string","minLength":1,"maxLength":200},"base_revision_id":{"description":"The revision you read (get_entry → editing.baseRevisionId). If the entry has changed since, the edit is refused as a conflict. Left out, the current revision is used.","type":"string","pattern":"^[0-9]+$"},"idempotency_key":{"description":"Optional: a key you choose (1–255 printable ASCII, e.g. a UUID), sent as the write’s Idempotency-Key. Calling again with the same key and the same change within 24 hours answers the first call’s result (replayed: true) and writes nothing; the same key with another change is refused (422).","type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["entry"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"update_entry_handles","title":"Add or remove handles","description":"Add handles to an entry or remove them — a live edit. Only the handles named are sent (add_handles / remove_handles), and the server applies them to the entry’s list as it is when it saves: an added handle goes at the end, one the entry has already, or one to remove that it has not, changes nothing (the answer’s handle_ops says which), and nothing is read first, so a handle someone else saved meanwhile is never undone. A handle you remove that the Wikidata sync supplied is held back so the sync does not bring it back. Dry run by default. To ask a reviewer instead, use propose_entry_change.","write":true,"confirm":false,"capabilities":[],"role":null,"keyless":false,"liveEdit":true,"linkImages":false,"input":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"add":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."},"display":{"description":"Text to show instead of the value.","type":"string","maxLength":200}},"required":["platform_id","value"]}},"remove":{"description":"Handles to take off, each by its value — or, for an email this key may not see, by {platform_id: \"email\", ref}.","maxItems":50,"type":"array","items":{"anyOf":[{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."}},"required":["platform_id","value"]},{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The masked handle’s platform: \"email\"."},"ref":{"type":"string","pattern":"^[0-9]{1,18}$","description":"A masked handle’s `ref` (get_entry → editing.handles, where its value is null): it names the stored handle without its address."}},"required":["platform_id","ref"]}]}},"summary":{"description":"Why you made this edit, in a few plain words; shown in the entry’s public history. At most 200 characters. Never an internal tracking ID (a backlog or ticket number); a VHID is fine.","type":"string","minLength":1,"maxLength":200},"base_revision_id":{"description":"Not needed, and not sent: handles are added and removed against the entry as it is when the edit is saved.","type":"string","pattern":"^[0-9]+$"},"idempotency_key":{"description":"Optional: a key you choose (1–255 printable ASCII, e.g. a UUID), sent as the write’s Idempotency-Key. Calling again with the same key and the same change within 24 hours answers the first call’s result (replayed: true) and writes nothing; the same key with another change is refused (422).","type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["entry"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"update_entries","title":"Edit many entries live","description":"Live edits to up to 25 entries in one request, each as update_entry_fields and update_entry_handles make one (the text fields, handles added and removed): a new revision each, audited with one batch id. Whether this key may edit each entry live is decided entry by entry, as for one edit (the dry run’s may_edit_live). By default each edit stands alone and the answer gives each one’s outcome; with atomic: true, any refused edit means none is made. Dry run by default: it reads each entry and lists what would change. The real run needs confirm set to the batch digest the dry run names. To ask a reviewer instead, use propose_entry_changes.","write":true,"confirm":true,"capabilities":[],"role":null,"keyless":false,"liveEdit":true,"linkImages":false,"input":{"type":"object","properties":{"edits":{"minItems":1,"maxItems":25,"type":"array","items":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"title":{"type":"string","minLength":1,"maxLength":500},"subtitle":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"description":{"anyOf":[{"type":"string","maxLength":65536},{"type":"null"}]},"short":{"description":"The short name in the entry’s URL; null removes it (the VHID URL stays).","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"remove_wikipedia_credit":{"description":"true: remove the “from Wikipedia” credit from the description.","type":"boolean"},"add_handles":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."},"display":{"description":"Text to show instead of the value.","type":"string","maxLength":200}},"required":["platform_id","value"]}},"remove_handles":{"description":"Handles to take off, each by its value — or, for an email this key may not see, by {platform_id: \"email\", ref}.","maxItems":50,"type":"array","items":{"anyOf":[{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."}},"required":["platform_id","value"]},{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The masked handle’s platform: \"email\"."},"ref":{"type":"string","pattern":"^[0-9]{1,18}$","description":"A masked handle’s `ref` (get_entry → editing.handles, where its value is null): it names the stored handle without its address."}},"required":["platform_id","ref"]}]}},"base_revision_id":{"description":"The revision you read (get_entry → editing.baseRevisionId). If the entry has changed since, the edit is refused as a conflict. Left out, the current revision is used.","type":"string","pattern":"^[0-9]+$"}},"required":["entry"]},"description":"The edits, each to a different entry."},"atomic":{"default":false,"description":"true: all or nothing — if any edit is refused, none is made (the route answers 422 naming each refused edit). false (the default): each edit on its own.","type":"boolean"},"summary":{"description":"Why you made these edits, in a few plain words: each edit’s summary unless it gives its own. Shown in each entry’s public history. Never an internal tracking ID; a VHID is fine.","type":"string","minLength":1,"maxLength":200},"confirm":{"description":"Needed with dry_run: false: this change’s exact target, as the dry run gives it in `confirm`.","type":"string","maxLength":300},"idempotency_key":{"description":"Optional: a key you choose (1–255 printable ASCII, e.g. a UUID), sent as the write’s Idempotency-Key. Calling again with the same key and the same change within 24 hours answers the first call’s result (replayed: true) and writes nothing; the same key with another change is refused (422).","type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["edits"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"propose_entry_changes","title":"Propose changes to many entries","description":"Proposals for up to 25 entries in one request, each as propose_entry_change files one (the text fields, handles added and removed) for a reviewer to decide; nothing changes until a person approves each. Audited, with one batch id. Proposing the same change again while it waits answers the same proposal. By default each proposal stands alone; with atomic: true, any refused one means none is filed. Dry run by default: it reads each entry and lists what would be proposed.","write":true,"confirm":false,"capabilities":["propose"],"role":null,"keyless":false,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"edits":{"minItems":1,"maxItems":25,"type":"array","items":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"title":{"type":"string","minLength":1,"maxLength":500},"subtitle":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"description":{"anyOf":[{"type":"string","maxLength":65536},{"type":"null"}]},"short":{"description":"The short name in the entry’s URL; null removes it (the VHID URL stays).","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"remove_wikipedia_credit":{"description":"true: remove the “from Wikipedia” credit from the description.","type":"boolean"},"add_handles":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."},"display":{"description":"Text to show instead of the value.","type":"string","maxLength":200}},"required":["platform_id","value"]}},"remove_handles":{"description":"Handles to take off, each by its value — or, for an email this key may not see, by {platform_id: \"email\", ref}.","maxItems":50,"type":"array","items":{"anyOf":[{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."}},"required":["platform_id","value"]},{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The masked handle’s platform: \"email\"."},"ref":{"type":"string","pattern":"^[0-9]{1,18}$","description":"A masked handle’s `ref` (get_entry → editing.handles, where its value is null): it names the stored handle without its address."}},"required":["platform_id","ref"]}]}},"base_revision_id":{"description":"The revision you read (get_entry → editing.baseRevisionId). If the entry has changed since, the edit is refused as a conflict. Left out, the current revision is used.","type":"string","pattern":"^[0-9]+$"}},"required":["entry"]},"description":"The proposals, each to a different entry."},"atomic":{"default":false,"description":"true: all or nothing — if any edit is refused, none is made (the route answers 422 naming each refused edit). false (the default): each edit on its own.","type":"boolean"},"idempotency_key":{"description":"Optional: a key you choose (1–255 printable ASCII, e.g. a UUID), sent as the write’s Idempotency-Key. Calling again with the same key and the same change within 24 hours answers the first call’s result (replayed: true) and writes nothing; the same key with another change is refused (422).","type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["edits"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"set_wikidata_ownership","title":"Hand fields or handles to Wikidata, or take them back","description":"Say who manages an entry’s fields and handles. relinquish_* hands them to the Wikidata sync (the next sync refreshes them, and a relinquish queues one); takeover_* takes them back, so the sync leaves them alone. release_held_handles lets the sync add back handles that were held back after a removal. A live edit, audited. Dry run by default. To ask a reviewer instead, use propose_entry_change.","write":true,"confirm":false,"capabilities":[],"role":null,"keyless":false,"liveEdit":true,"linkImages":false,"input":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"relinquish_fields":{"maxItems":5,"type":"array","items":{"type":"string","enum":["title","subtitle","description","image","entity_type"]}},"takeover_fields":{"maxItems":5,"type":"array","items":{"type":"string","enum":["title","subtitle","description","image","entity_type"]}},"relinquish_handles":{"description":"Only while Wikidata lists the value.","maxItems":50,"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."}},"required":["platform_id","value"]}},"takeover_handles":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."}},"required":["platform_id","value"]}},"release_held_handles":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."}},"required":["platform_id","value"]}},"summary":{"description":"Why you made this edit, in a few plain words; shown in the entry’s public history. At most 200 characters. Never an internal tracking ID (a backlog or ticket number); a VHID is fine.","type":"string","minLength":1,"maxLength":200},"base_revision_id":{"description":"The revision you read (get_entry → editing.baseRevisionId). If the entry has changed since, the edit is refused as a conflict. Left out, the current revision is used.","type":"string","pattern":"^[0-9]+$"},"idempotency_key":{"description":"Optional: a key you choose (1–255 printable ASCII, e.g. a UUID), sent as the write’s Idempotency-Key. Calling again with the same key and the same change within 24 hours answers the first call’s result (replayed: true) and writes nothing; the same key with another change is refused (422).","type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["entry"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"set_entry_image","title":"Set or clear an entry’s image","description":"Put an image on an entry — one already on the site (`media_id`, from media_catalog or get_entry) or a Wikimedia Commons file (`commons_token`, from search_commons_images) — or take the image off (`clear: true`). A new image starts with no link; `link` sets the address the image opens on the entry page instead of the full-screen viewer (`\"\"` clears it), which needs Link images. A live edit, audited. Setting the image yourself takes it from the Wikidata sync. Dry run by default. To ask a reviewer instead, use propose_entry_change.","write":true,"confirm":false,"capabilities":[],"role":null,"keyless":false,"liveEdit":true,"linkImages":true,"input":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"media_id":{"type":"string","pattern":"^[0-9]{1,18}$"},"commons_token":{"type":"string","minLength":1,"maxLength":2000},"clear":{"type":"boolean"},"link":{"description":"An absolute https:// (or http://) address the image links to instead of opening the full-screen viewer; \"\" for no link. Needs Link images.","type":"string","maxLength":2048},"summary":{"description":"Why you made this edit, in a few plain words; shown in the entry’s public history. At most 200 characters. Never an internal tracking ID (a backlog or ticket number); a VHID is fine.","type":"string","minLength":1,"maxLength":200},"base_revision_id":{"description":"The revision you read (get_entry → editing.baseRevisionId). If the entry has changed since, the edit is refused as a conflict. Left out, the current revision is used.","type":"string","pattern":"^[0-9]+$"},"idempotency_key":{"description":"Optional: a key you choose (1–255 printable ASCII, e.g. a UUID), sent as the write’s Idempotency-Key. Calling again with the same key and the same change within 24 hours answers the first call’s result (replayed: true) and writes nothing; the same key with another change is refused (422).","type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["entry"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"set_entry_gallery","title":"Set an entry’s gallery","description":"Set the images an entry shows after its lead image: the whole ordered list of media ids (from media_catalog or get_entry), replacing the gallery; [] empties it. At most 5 (6 images in all with the lead), each once, never the lead itself (set_entry_image sets that). An image taken off loses its link; `links` sets or clears the link of an image in the list (needs Link images). A live edit, audited. Dry run by default. To ask a reviewer instead, use propose_entry_change.","write":true,"confirm":false,"capabilities":[],"role":null,"keyless":false,"liveEdit":true,"linkImages":true,"input":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"media_ids":{"maxItems":50,"type":"array","items":{"type":"string","pattern":"^[0-9]{1,18}$"}},"links":{"description":"media id → the address that image links to on the entry page instead of the full-screen viewer; \"\" clears it. Images not named keep theirs. Needs Link images.","type":"object","propertyNames":{"type":"string","pattern":"^[0-9]{1,18}$"},"additionalProperties":{"type":"string","maxLength":2048}},"summary":{"description":"Why you made this edit, in a few plain words; shown in the entry’s public history. At most 200 characters. Never an internal tracking ID (a backlog or ticket number); a VHID is fine.","type":"string","minLength":1,"maxLength":200},"base_revision_id":{"description":"The revision you read (get_entry → editing.baseRevisionId). If the entry has changed since, the edit is refused as a conflict. Left out, the current revision is used.","type":"string","pattern":"^[0-9]+$"},"idempotency_key":{"description":"Optional: a key you choose (1–255 printable ASCII, e.g. a UUID), sent as the write’s Idempotency-Key. Calling again with the same key and the same change within 24 hours answers the first call’s result (replayed: true) and writes nothing; the same key with another change is refused (422).","type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["entry","media_ids"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"propose_entry_revert","title":"Propose putting an entry back to an earlier revision","description":"Propose, for a reviewer to decide, putting an entry back as it was at one of its revisions (get_entry_history: a `ref_id` like \"r123\"; imported legacy revisions cannot be put back). The proposal holds only what differs from now — title, subtitle, description, short name, handles and their order, lead image, gallery — as propose_entry_change would send it; who manages each value (Wikidata or the site) is left as it is. An image withdrawn since is left out, and the answer names it. Refused (nothing_to_change) when the entry already matches that revision. `note` tells the reviewer why. Audited. Dry run by default.","write":true,"confirm":false,"capabilities":["propose"],"role":null,"keyless":false,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"revision":{"type":"string","pattern":"^r[0-9]+$","description":"The revision’s ref_id, e.g. \"r123\"."},"note":{"description":"Why you propose this, in plain words: posted as the proposal’s first comment, which the reviewer reads with it. At most 2,000 characters. Never an internal tracking ID (a backlog or ticket number); a VHID is fine.","type":"string","minLength":1,"maxLength":2000},"idempotency_key":{"description":"Optional: a key you choose (1–255 printable ASCII, e.g. a UUID), sent as the write’s Idempotency-Key. Calling again with the same key and the same change within 24 hours answers the first call’s result (replayed: true) and writes nothing; the same key with another change is refused (422).","type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["entry","revision"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"propose_entry_change","title":"Propose a change to an entry","description":"Propose a change for a reviewer to decide — for any entry you may propose to, including ones you could edit live; nothing changes until a person approves it. Give only what changes; null clears a field. `qid` proposes linking the entry to a Wikidata item, or a different one (null: unlink it), with `adopt` naming the fields the item may manage (left out: the server’s default on a first link, unchanged on a re-point); only a reviewer with Manage entries may approve that. `note` tells the reviewer why. Audited. Dry run by default: it names the entries already on the item.","write":true,"confirm":false,"capabilities":["propose"],"role":null,"keyless":false,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"title":{"type":"string","minLength":1,"maxLength":500},"subtitle":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"description":{"anyOf":[{"type":"string","maxLength":65536},{"type":"null"}]},"short":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"remove_wikipedia_credit":{"description":"true: remove the “from Wikipedia” credit from the description.","type":"boolean"},"credit_wikipedia_page":{"description":"Credit the description “from Wikipedia”: an English Wikipedia article or draft, by title (\"Draft:Dominic McLaughlin\") or address. The server reads the page (redirects followed) and refuses it unless at least half of the description’s three-word phrases are the page’s lead (credit_text_differs, with the share).","type":"string","minLength":1,"maxLength":2000},"credit_let_wikidata_manage":{"description":"With a draft named in credit_wikipedia_page: false keeps the description site-authored. By default a draft’s credit hands the description to the Wikidata sync, which leaves it alone until the draft is an article linked from the entry’s Wikidata item.","type":"boolean"},"add_handles":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."},"display":{"type":"string","maxLength":200}},"required":["platform_id","value"]}},"remove_handles":{"description":"Handles to take off, each by its value — or, for an email this key may not see (value null in get_entry’s editing.handles), by {platform_id: \"email\", ref}.","maxItems":50,"type":"array","items":{"anyOf":[{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."}},"required":["platform_id","value"]},{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The masked handle’s platform: \"email\"."},"ref":{"type":"string","pattern":"^[0-9]{1,18}$","description":"A masked handle’s `ref` (get_entry → editing.handles, where its value is null): it names the stored handle without its address."}},"required":["platform_id","ref"]}]}},"handle_order":{"description":"Every handle, in the order the entry should show them: exactly the entry’s handles after add_handles and remove_handles, each once (a masked email by its ref).","maxItems":100,"type":"array","items":{"anyOf":[{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."}},"required":["platform_id","value"]},{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The masked handle’s platform: \"email\"."},"ref":{"type":"string","pattern":"^[0-9]{1,18}$","description":"A masked handle’s `ref` (get_entry → editing.handles, where its value is null): it names the stored handle without its address."}},"required":["platform_id","ref"]}]}},"image_media_id":{"description":"A new lead image already on the site (media_catalog, get_entry), or one you uploaded held for this proposal (upload_image or upload_image_from_url with hold: true): only its uploader may propose a held image, and the approval releases it.","type":"string","pattern":"^[0-9]{1,18}$"},"image_commons_token":{"description":"A new lead image from Wikimedia Commons: a hit’s `token` from search_commons_images. The file is recorded on the site first, as the editor’s picker does.","type":"string","minLength":1,"maxLength":2000},"clear_image":{"description":"true: take the lead image off. At most one of image_media_id, image_commons_token and clear_image.","type":"boolean"},"gallery":{"description":"The images after the lead: the whole ordered list of media ids, replacing the gallery; [] empties it. At most 5, each once, never the lead.","maxItems":50,"type":"array","items":{"type":"string","pattern":"^[0-9]{1,18}$"}},"relinquish_fields":{"description":"Fields to hand to the Wikidata sync.","maxItems":5,"type":"array","items":{"type":"string","enum":["title","subtitle","description","image","entity_type"]}},"takeover_fields":{"description":"Fields to take back from the Wikidata sync, keeping their values.","maxItems":5,"type":"array","items":{"type":"string","enum":["title","subtitle","description","image","entity_type"]}},"relinquish_handles":{"description":"Handles to hand to the Wikidata sync; only while Wikidata lists the value.","maxItems":50,"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."}},"required":["platform_id","value"]}},"takeover_handles":{"description":"Handles to take back from the Wikidata sync.","maxItems":50,"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."}},"required":["platform_id","value"]}},"release_held_handles":{"description":"Held-back handles the sync may add back.","maxItems":50,"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."}},"required":["platform_id","value"]}},"qid":{"description":"The Wikidata item to link the entry to, e.g. \"Q42\" (a different one re-points it); null unlinks it.","anyOf":[{"type":"string","pattern":"^[Qq][1-9][0-9]*$"},{"type":"null"}]},"adopt":{"description":"With qid: the fields the item may manage; the rest are taken back by the site.","maxItems":5,"type":"array","items":{"type":"string","enum":["title","subtitle","description","image","entity_type"]}},"base_revision_id":{"description":"The revision you read (get_entry → editing.baseRevisionId). If the entry has changed since, the edit is refused as a conflict. Left out, the current revision is used.","type":"string","pattern":"^[0-9]+$"},"note":{"description":"Why you propose this, in plain words: posted as the proposal’s first comment, which the reviewer reads with it. At most 2,000 characters. Never an internal tracking ID (a backlog or ticket number); a VHID is fine.","type":"string","minLength":1,"maxLength":2000},"idempotency_key":{"description":"Optional: a key you choose (1–255 printable ASCII, e.g. a UUID), sent as the write’s Idempotency-Key. Calling again with the same key and the same change within 24 hours answers the first call’s result (replayed: true) and writes nothing; the same key with another change is refused (422).","type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["entry"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"propose_new_entry","title":"Propose a new entry","description":"Propose a new entry for a reviewer to decide: it waits in Console → Applications under Quick proposals (list_applications with tab \"proposals\") and becomes live when approved. Refused (not_accepting) while Settings pauses new-entry requests, unless this account may create entries itself. For someone or something with no Wikidata item, leave out qid. With qid, the entry is linked to that item, and after approval Wikidata keeps up to date whatever you did not write (and any field in let_wikidata_manage). Refused, naming the entry, when one is already on the item. A merged-away QID is refused (409 qid_merged), naming the item Wikidata kept in wikidata_item: propose on that one. Sending the same proposal again within 15 minutes answers with the first one. `note` tells the reviewer why. Audited. Dry run by default: it names the entries already on the item, a short name already taken or held by a site redirect (short_is_redirect), and handles already on another entry.","write":true,"confirm":false,"capabilities":["propose"],"role":null,"keyless":false,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":500},"subtitle":{"type":"string","maxLength":500},"description":{"description":"HTML; it is sanitised when stored.","type":"string","maxLength":65536},"short":{"description":"The short name, as in verifiedhandles.org/<short>. Whether it is free is checked when the proposal is approved.","type":"string","maxLength":100},"handles":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","minLength":1,"description":"The platform id, e.g. \"twitter\": one the platform catalog has (get_entry with include_platforms lists the enabled ones; platform_catalog, every one). A new handle on an unknown or disabled platform is refused (400 invalid_platform)."},"value":{"type":"string","minLength":1,"description":"The handle as stored, e.g. \"nasa\"."},"display":{"type":"string","maxLength":200}},"required":["platform_id","value"]}},"qid":{"description":"The Wikidata item, e.g. \"Q42\". Leave it out for an entry with no item.","type":"string","pattern":"^[Qq][1-9][0-9]*$"},"let_wikidata_manage":{"description":"With qid: fields you give that Wikidata should keep up to date after approval, as the console form does for a title it prefilled and you left as it was.","maxItems":3,"type":"array","items":{"type":"string","enum":["title","subtitle","description"]}},"note":{"description":"Why you propose this, in plain words: posted as the proposal’s first comment, which the reviewer reads with it. At most 2,000 characters. Never an internal tracking ID (a backlog or ticket number); a VHID is fine.","type":"string","minLength":1,"maxLength":2000},"idempotency_key":{"description":"Optional: a key you choose (1–255 printable ASCII, e.g. a UUID), sent as the write’s Idempotency-Key. Calling again with the same key and the same change within 24 hours answers the first call’s result (replayed: true) and writes nothing; the same key with another change is refused (422).","type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["title"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"report_entry","title":"Report an entry","description":"Report an entry to the Verified Handles team, as its page’s “Report” form does: for a person to decide: delete, merge, image rights, wrong information. Reason `image` names the image it is about (media_id, one of the images the entry shows) and needs a note saying who owns it and what should change. Reason `image_link` is about where an image links (only for an image with a link — get_entry’s images[].link); media_id names which, else the first with one, and the report keeps the link as it is now. To delete or merge an entry, report it as `incorrect` or `other` with a note naming the entry to keep. It lands in Console → Reports. Audited. Dry run by default.","write":true,"confirm":false,"capabilities":[],"role":null,"keyless":false,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"reason":{"type":"string","enum":["impersonation","incorrect","outdated","abuse","image","image_link","other"],"description":"impersonation: a handle here isn’t the real account; incorrect information; outdated: a handle has changed or closed; abuse or inappropriate content; image: its rights or permission; image_link: where an image links goes somewhere wrong, misleading or harmful; other."},"note":{"description":"What is wrong, for the team (at most 1000 characters). Needed for an image report.","type":"string","maxLength":1000},"media_id":{"description":"For reason `image` or `image_link`: which of the entry’s images (the dry run lists them).","type":"string","pattern":"^[0-9]{1,18}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["entry","reason"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"list_my_submissions","title":"My own submissions","description":"This key’s own account’s submissions — proposed changes, new-entry proposals, claims, reports and applications — newest first, 25 a page: each with its status, its entry, when it was sent and decided, and the reviewer’s notes. `status: open` keeps the ones still waiting. With `id`, one submission (yours, or any a reviewer may see) with what it asks and its comment thread.","write":false,"confirm":false,"capabilities":[],"role":null,"keyless":false,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"status":{"default":"all","type":"string","enum":["open","all"]},"page":{"default":1,"type":"integer","minimum":1,"maximum":10000},"id":{"description":"One submission’s id, from this list or from the tool that sent it.","type":"string","pattern":"^[0-9]{1,18}$"}}},"output":{"type":"object","properties":{"status":{"anyOf":[{"type":"string","enum":["open","all"]},{"type":"string","enum":["pending","needs_info","approved","rejected","merged","spam","superseded","retracted","resolved"]}]},"page":{"type":"integer","minimum":1,"maximum":9007199254740991},"per_page":{"type":"number","const":25},"total":{"type":"integer","minimum":0,"maximum":9007199254740991},"has_next":{"type":"boolean"},"submissions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"kind":{"type":"string","enum":["new_entity","correction","claim","report","application"],"description":"What was asked: a new entry, a change, a claim, a report or an application."},"status":{"type":"string","enum":["pending","needs_info","approved","rejected","merged","spam","superseded","retracted","resolved"]},"entry":{"anyOf":[{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string"}},"required":["vhid","short","title"],"additionalProperties":{}},{"type":"null"}],"description":"The entry it is about; null for a new entry or an application."},"qid":{"description":"The Wikidata item it names, if any.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"decided_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}]},"review_notes":{"description":"What the reviewer wrote when deciding it.","type":["string","null"]}},"required":["id","kind","status","entry","qid","created_at","decided_at","review_notes"],"additionalProperties":{},"description":"One thing you sent for review, as your submissions list shows it."}},"id":{"type":"string","pattern":"^\\d+$"},"kind":{"type":"string","enum":["new_entity","correction","claim","report","application"],"description":"What was asked: a new entry, a change, a claim, a report or an application."},"entry":{"anyOf":[{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string"}},"required":["vhid","short","title"],"additionalProperties":{}},{"type":"null"}],"description":"The entry it is about; null for a new entry or an application."},"qid":{"description":"The Wikidata item it names, if any.","type":["string","null"]},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"decided_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}]},"review_notes":{"description":"What the reviewer wrote when deciding it.","type":["string","null"]},"mine":{"type":"boolean","description":"You sent it."},"payload":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"What it asks for, in its kind’s shape: for `correction`, `new_entity` and `application`, an `EditPayload` (the fields to set, and for a revert or an approval the server’s own); for `report`, `{reason, note?, media_id?, link?}`; for `claim`, `{method}`."},"comments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"author_name":{"description":"The writer’s name, where you may see it.","type":["string","null"]},"author_role":{"description":"`reviewer` for a reviewer’s comment; null for the submitter’s own.","type":["string","null"]},"body":{"type":"string"},"created_at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"}},"required":["id","author_name","author_role","body","created_at"],"additionalProperties":{}}}},"required":["status"],"additionalProperties":{}}},{"name":"retract_my_submission","title":"Retract my own submission","description":"Withdraw one of this key’s own account’s submissions while it is still open (waiting for a reviewer, or for your answer): no reviewer acts on it after, and it cannot be reopened. Only your own, whatever the key’s role. Audited. Dry run by default: it says whether the submission is yours and still open.","write":true,"confirm":false,"capabilities":[],"role":null,"keyless":false,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"id":{"type":"string","pattern":"^[0-9]{1,18}$","description":"The submission’s id (list_my_submissions, or the tool that sent it)."},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["id"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"comment_on_my_submission","title":"Comment on my own submission","description":"Add a message to one of this key’s own account’s submissions (or, for a reviewer, any they may see): the reviewer reads it with the submission, and the submitter sees it on theirs. Answering a question a reviewer asked on your application sends it back to them. Audited. Dry run by default.","write":true,"confirm":false,"capabilities":[],"role":null,"keyless":false,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"id":{"type":"string","pattern":"^[0-9]{1,18}$","description":"The submission’s id (list_my_submissions, or the tool that sent it)."},"text":{"type":"string","minLength":1,"maxLength":2000,"description":"The message, in plain words; at most 2,000 characters."},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["id","text"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"revoke_api_key","title":"Revoke an API key","description":"Revoke an API key: anything using it stops working at once, and it cannot be brought back (its owner makes a new one). One of your own account’s keys, or — with `sub`, and the permission to manage users — one of another account’s. The dry run lists the account’s keys (never more than a key’s public prefix) and says if it is the key this call is made with. Audited. Dry run by default; the real run needs confirm set to the key’s id.","write":true,"confirm":true,"capabilities":[],"role":null,"keyless":false,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"key_id":{"type":"string","pattern":"^[0-9]{1,18}$","description":"The key’s id, as the dry run lists it."},"sub":{"description":"Another account’s id (`sub`), to revoke one of its keys. Left out: one of your own.","type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)"},"confirm":{"description":"Needed with dry_run: false: this change’s exact target, as the dry run gives it in `confirm`.","type":"string","maxLength":300},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["key_id"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}},{"name":"propose_from_wikidata","title":"Propose what a Wikidata item says","description":"Propose, for a reviewer to decide, what a Wikidata item says for an entry: its identifier handles, and when asked its label (as the title), its description (as the subtitle) and its image. `qid` defaults to the item the entry is linked to. For that item, unless the entry is protected or claimed, it proposes handing the values to the Wikidata sync instead of copying them (the sync then writes and keeps them up to date), and says so. For another item it copies the values, which become the site’s own: the sync will not keep them up to date. Handles the entry already has (by each platform’s own rule for letter case) are left out. The entry’s description comes from Wikipedia, not from the item, so it is only ever handed to the sync. Warns when the item is another entry’s. `note` tells the reviewer why. Audited. Dry run by default: it shows what the item has, what the entry has, and the proposal.","write":true,"confirm":false,"capabilities":["propose"],"role":null,"keyless":false,"liveEdit":false,"linkImages":false,"input":{"type":"object","properties":{"entry":{"type":"string","minLength":1,"maxLength":200,"pattern":"^(?!\\.{1,2}$)","description":"The entry: its VHID (11 digits), its short name (as in verifiedhandles.org/<short>), or its Wikidata QID (Q42)."},"qid":{"description":"The Wikidata item, e.g. \"Q42\". Default: the item the entry is linked to.","type":"string","pattern":"^[Qq][1-9][0-9]*$"},"take":{"description":"What to take from the item: \"handles\" (the default), \"title\" (its label), \"subtitle\" (its description), \"description\" (only by handing it to the sync), \"image\".","minItems":1,"maxItems":5,"type":"array","items":{"type":"string","enum":["handles","title","subtitle","description","image"]}},"note":{"description":"Why you propose this, in plain words: posted as the proposal’s first comment, which the reviewer reads with it. At most 2,000 characters. Never an internal tracking ID (a backlog or ticket number); a VHID is fine.","type":"string","minLength":1,"maxLength":2000},"idempotency_key":{"description":"Optional: a key you choose (1–255 printable ASCII, e.g. a UUID), sent as the write’s Idempotency-Key. Calling again with the same key and the same change within 24 hours answers the first call’s result (replayed: true) and writes nothing; the same key with another change is refused (422).","type":"string","pattern":"^[\\x20-\\x7E]{1,255}$"},"dry_run":{"default":true,"description":"true (the default): write nothing — show the change and the exact request it would send. false: make the change.","type":"boolean"}},"required":["entry"]},"output":{"type":"object","properties":{"dry_run":{"description":"true: a dry run, which wrote nothing. Absent from a real run.","type":"boolean"},"would_send":{"description":"A dry run: the exact request the real run would send (`method`, `path`, any `headers` such as an Idempotency-Key, and its `body`, `form` or `query`), or the requests, in order."},"note":{"description":"A dry run: what to do next to make the change, in words."},"confirm":{"description":"A dry run of a change that needs it: the exact target to send back as `confirm` with dry_run: false.","type":"string"},"replayed":{"description":"A real run with an idempotency_key already used for this change: the first call’s answer, given back; nothing was written now.","type":"boolean","const":true},"may_edit_live":{"description":"A dry run of a live edit: whether this key’s account may edit this entry live.","type":"boolean"},"refused_because":{"description":"A dry run the real run would be refused for: why, in words.","type":"string"},"result":{"description":"A real run: the route’s own answer, where the tool adds facts of its own beside it."}},"additionalProperties":{},"description":"A dry run (`dry_run: true`): what the change would be and the exact request it would send. A real run: what the route answered."}}]},"components":{"schemas":{"AccountData":{"type":"object","properties":{"format":{"type":"string","const":"verifiedhandles-account-data"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Bumped when a field changes meaning or goes; a new field does not bump it."},"exported_at":{"type":"string","format":"date-time"},"account":{"type":"object","properties":{"id":{"type":"string","description":"Your account’s id (its OIDC `sub`)."},"email":{"type":["string","null"]},"display_name":{"type":["string","null"]},"role":{"type":"string","enum":["registered","trusted_editor","admin"]},"blocked":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"last_sign_in":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"role_granted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"capability_overrides":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"boolean"},"description":"Capabilities granted (true) or taken away (false) beyond your role’s."},"preferences":{"type":"object","properties":{"copy_handle_only":{"type":"boolean"},"email_notifications":{"type":"boolean"},"save_recent_searches":{"type":"boolean"},"name_visibility":{"type":"string","enum":["public","trusted","admins"]}},"required":["copy_handle_only","email_notifications","save_recent_searches","name_visibility"],"additionalProperties":false}},"required":["id","email","display_name","role","blocked","created_at","last_sign_in","role_granted_at","capability_overrides","preferences"],"additionalProperties":false},"submissions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"kind":{"type":"string","enum":["new_entity","correction","claim","report","application"]},"status":{"type":"string","enum":["pending","needs_info","approved","rejected","merged","spam","superseded","retracted","resolved"]},"entry_vhid":{"type":["string","null"]},"entry_title":{"type":["string","null"]},"qid":{"type":["string","null"]},"payload":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"What it asks for, in its kind’s shape: for `correction`, `new_entity` and `application`, an `EditPayload` (the fields to set, and for a revert or an approval the server’s own); for `report`, `{reason, note?, media_id?, link?}`; for `claim`, `{method}`."},"evidence":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"The case it carries, in its kind’s shape: an application’s eligibility case, a claim’s proof; null for the rest."},"review_notes":{"type":["string","null"]},"ip":{"description":"The address you sent it from; erased after a year.","type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","kind","status","entry_vhid","entry_title","qid","payload","evidence","review_notes","ip","created_at","decided_at"],"additionalProperties":false}},"comments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"submission_id":{"type":"string","pattern":"^\\d+$"},"on_your_submission":{"type":"boolean"},"body":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"author":{"type":"string","description":"`you`, or the writer as a signed-out visitor would see them."}},"required":["id","submission_id","on_your_submission","body","created_at","author"],"additionalProperties":false},"description":"Every comment on your submissions, and those you wrote on anyone else’s."},"notes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"entry_vhid":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","entry_vhid","body","created_at","updated_at"],"additionalProperties":false},"description":"The internal notes you wrote."},"api_keys":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"prefix":{"type":"string"},"role_ceiling":{"type":"string","enum":["registered","trusted_editor","admin"]},"read_only":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["name","prefix","role_ceiling","read_only","created_at","expires_at","last_used_at","revoked_at"],"additionalProperties":false},"description":"Your keys, never a secret. Not your connected apps’ keys: they are under `connected_apps`."},"connected_apps":{"type":"array","items":{"type":"object","properties":{"client_id":{"type":"string"},"client_name":{"type":"string"},"redirect_uri":{"type":"string"},"resource":{"type":"string"},"role_ceiling":{"type":"string","enum":["registered","trusted_editor","admin"]},"read_only":{"type":"boolean"},"days":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"created_at":{"type":"string","format":"date-time"},"connected_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"last_refreshed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"key_prefix":{"type":["string","null"]},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["client_id","client_name","redirect_uri","resource","role_ceiling","read_only","days","created_at","connected_at","last_refreshed_at","key_prefix","expires_at","revoked_at"],"additionalProperties":false},"description":"The apps you connected with OAuth (and consents not yet completed), never a token."},"wikimedia_account":{"anyOf":[{"type":"object","properties":{"wiki":{"type":"string","enum":["wikidata","testwikidata"]},"username":{"type":"string"},"grants":{"type":"array","items":{"type":"string"}},"linked_at":{"type":"string","format":"date-time"},"checked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["wiki","username","grants","linked_at","checked_at"],"additionalProperties":false},{"type":"null"}],"description":"The Wikimedia account linked to yours (never its token), or null."},"drafts":{"type":"array","items":{"type":"object","properties":{"entry_vhid":{"type":"string"},"entry_title":{"type":"string"},"base_revision_id":{"anyOf":[{"type":"string","pattern":"^\\d+$"},{"type":"null"}]},"draft":{"$ref":"#/components/schemas/EntryDraft"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"}},"required":["entry_vhid","entry_title","base_revision_id","draft","created_at","updated_at","expires_at"],"additionalProperties":false},"description":"Your unsaved entry drafts, each whole, as kept."},"audit":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"ts":{"type":"string","format":"date-time"},"action":{"type":"string"},"target_type":{"type":"string"},"target_id":{"type":"string"},"detail":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"What changed, in the action’s own shape (lib/audit.ts names each action’s): it varies by action, and a row keeps the shape it was written with. A row written with an API key also carries `key` (its id), `via: \"api_key\"` where the writer named no surface, and `mcp_tool` when an MCP tool made the request."},"by":{"type":"string","description":"`you`, `Verified Handles (automatic)`, or the actor as a signed-out visitor would see them."},"ip":{"description":"On your own rows only: the address it came from, until it is erased after a year.","type":["string","null"]},"surface":{"description":"On your own rows only: what it came through (see the audit log).","anyOf":[{"type":"string","enum":["editor","console","api","mcp","site","worker","migration"]},{"type":"null"}]},"api_key_id":{"description":"On your own rows only: your API key it was made with, if any.","anyOf":[{"type":"string","pattern":"^\\d+$"},{"type":"null"}]},"mcp_tool":{"description":"On your own rows only: the MCP tool that made it, if one did.","type":["string","null"]},"user_agent":{"description":"On your own rows only: your browser or client, cut to 200 characters.","type":["string","null"]},"request_id":{"description":"On your own rows only: the request’s id.","type":["string","null"]}},"required":["id","ts","action","target_type","target_id","detail","by"],"additionalProperties":false},"description":"The audit rows by you or about you."},"edits":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"entry_vhid":{"type":"string"},"entry_title":{"type":"string"},"ts":{"type":"string","format":"date-time"},"source":{"type":"string"},"summary":{"type":["string","null"]},"made_by_you":{"type":"boolean"},"proposed_by_you":{"type":"boolean"},"hidden":{"type":"boolean"}},"required":["id","entry_vhid","entry_title","ts","source","summary","made_by_you","proposed_by_you","hidden"],"additionalProperties":false},"description":"Every revision you made or proposed: its summary (the change itself is on the entry’s history)."},"hidden_revisions":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["revision","legacy_revision"]},"id":{"type":"string","pattern":"^\\d+$"},"entry_vhid":{"type":["string","null"]},"hidden_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"hidden_reason":{"type":["string","null"]}},"required":["kind","id","entry_vhid","hidden_at","hidden_reason"],"additionalProperties":false},"description":"The revisions you hid."},"reviews":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"kind":{"type":"string","enum":["new_entity","correction","claim","report","application"]},"status":{"type":"string","enum":["pending","needs_info","approved","rejected","merged","spam","superseded","retracted","resolved"]},"entry_vhid":{"type":["string","null"]},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","kind","status","entry_vhid","decided_at"],"additionalProperties":false},"description":"The submissions you reviewed (never who sent them)."},"claimed_entries":{"type":"array","items":{"type":"object","properties":{"entry_vhid":{"type":"string"},"entry_title":{"type":"string"}},"required":["entry_vhid","entry_title"],"additionalProperties":false},"description":"The entries you have claimed."},"uploads":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"mime":{"type":["string","null"]},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"license":{"type":["string","null"]},"attribution":{"type":["string","null"]},"source_url":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"withdrawn_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","mime","width","height","license","attribution","source_url","created_at","withdrawn_at"],"additionalProperties":false},"description":"The images you uploaded."},"permission_history":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"ts":{"type":"string","format":"date-time"},"change":{"type":"string"},"about":{"type":["string","null"]},"by":{"type":"string"}},"required":["id","ts","change","about","by"],"additionalProperties":{}},"description":"Changes to your permissions, and the permission changes you made."},"wikidata_changes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"state":{"type":"string"}},"required":["id","state"],"additionalProperties":{}},"description":"The Wikidata changes you proposed, approved or snoozed."},"wikipedia_link_reviews":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"status":{"type":"string"}},"required":["id","status"],"additionalProperties":{}},"description":"The proposed Wikipedia links you took or left."},"sync_requests":{"type":"array","items":{"type":"object","properties":{"qid":{"type":"string"},"requested_at":{"type":"string","format":"date-time"}},"required":["qid","requested_at"],"additionalProperties":{}},"description":"The Wikidata syncs you asked for (forgotten after a year)."},"emails":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"kind":{"type":"string"},"subject":{"type":"string"},"body_text":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","kind","subject","body_text","created_at","sent_at"],"additionalProperties":false},"description":"The emails queued to your address in the last 30 days, sent or not."},"email_suppression":{"anyOf":[{"type":"object","properties":{"reason":{"type":"string","enum":["bounce","complaint"]},"detail":{"type":["string","null"]},"reports":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"first_at":{"type":"string","format":"date-time"},"last_at":{"type":"string","format":"date-time"}},"required":["reason","detail","reports","first_at","last_at"],"additionalProperties":false},{"type":"null"}],"description":"Why we stopped emailing your address, or null."},"sessions":{"type":"array","items":{"type":"object","properties":{"started_at":{"type":"string","format":"date-time"}},"required":["started_at"],"additionalProperties":false},"description":"When each of your live sign-in sessions began."}},"required":["format","version","exported_at","account","submissions","comments","notes","api_keys","connected_apps","wikimedia_account","drafts","audit","edits","hidden_revisions","reviews","claimed_entries","uploads","permission_history","wikidata_changes","wikipedia_link_reviews","sync_requests","emails","email_suppression","sessions"],"additionalProperties":false,"description":"Everything the site holds about one account."},"ApiKeyRefusal":{"type":"object","properties":{"error":{"type":"string","enum":["api_keys_disabled","bearer_with_cookie","bearer_not_accepted_here","invalid_api_key","invalid_token","api_key_revoked","api_key_expired","api_key_not_permitted","read_only_key","authorization_not_bearer"],"description":"`invalid_api_key`: the key is malformed or unknown. `api_key_revoked` / `api_key_expired`: it no longer works. `api_key_not_permitted`: its holder may no longer use keys. `bearer_with_cookie`: a request sent both a key and a session cookie (send one). `read_only_key`: a read-only key tried to write. `api_keys_disabled`: this instance accepts no keys. `bearer_not_accepted_here`: the sign-in routes refuse keys. `invalid_token`: an OAuth access token (`vho_…`) is malformed, unknown, superseded or past its hour, or was sent somewhere other than the MCP address it was issued for (then `error_description` names that address). `authorization_not_bearer`: a read sent an `Authorization` header that is not `Bearer` (send a key as `Authorization: Bearer`, or no header)."},"error_description":{"description":"With `invalid_token` for a token sent to the wrong address: which address it is for. With `authorization_not_bearer`: what to send instead.","type":"string"}},"required":["error"],"additionalProperties":false,"description":"A request whose `Authorization` header the key check refused: a `Bearer` key or token that does not work, or, on a read, another scheme. Nothing ran."},"ApiKeyRow":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"name":{"type":"string"},"prefix":{"type":"string","description":"The key’s public part, `vhk_<prefix>_…`; the secret is never shown again."},"state":{"type":"string","enum":["live","revoked","expired"]},"role_ceiling":{"type":"string","enum":["registered","trusted_editor","admin"],"description":"The key never acts above this role."},"read_only":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"this_key":{"type":"boolean","description":"It is the key making this request."}},"required":["id","name","prefix","state","role_ceiling","read_only","created_at","expires_at","last_used_at","revoked_at","this_key"],"additionalProperties":false,"description":"One API key, as its holder’s account page lists it. Never the secret."},"ApplicationCheck":{"type":"object","properties":{"qid":{"anyOf":[{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string"},"blocked":{"type":"boolean","description":"The entry was deleted."}},"required":["vhid","short","title","blocked"],"additionalProperties":false},{"type":"null"}],"description":"The entry on that item, or null: a live one first; else the entry one on it was merged into; else a deleted one."},"title":{"type":"array","items":{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string"}},"required":["vhid","short","title"],"additionalProperties":false},"description":"Entries whose names match, best first."}},"required":["qid","title"],"additionalProperties":false,"description":"Entries the site already has that an application may duplicate."},"ApplicationForm":{"type":"object","properties":{"title":{"type":"string","maxLength":200,"description":"The name the entry should carry. Required."},"subtitle":{"description":"A line under the name: what they are known for.","type":"string","maxLength":300},"description":{"type":"string","maxLength":4000},"qid":{"description":"The Wikidata item, `Q<n>`, if there is one. One entry per item.","type":"string"},"relationship":{"type":"string","enum":["self","representative","third_party"],"description":"How you are connected: it is you, you represent them, or neither. Required."},"handle_platform":{"description":"A handle’s platform id (the catalog’s `id`, e.g. `twitter`). At least one handle. Repeat the field for more than one; the nth of each goes together.","type":"string"},"handle_value":{"description":"The handle or address on that platform. Repeat the field for more than one; the nth of each goes together.","type":"string"},"wikipedia_url":{"description":"An article about them on en.wikipedia.org.","type":"string"},"risk_statement":{"description":"Why impersonation is a risk for them, in a sentence or two.","type":"string","maxLength":1000},"risk_link":{"description":"A link showing that risk. Repeat the field for more than one; the nth of each goes together.","type":"string"},"verified_platform":{"description":"`tiktok` or `snapchat`: a platform that has verified them. Repeat the field for more than one; the nth of each goes together.","type":"string"},"verified_handle":{"description":"Their verified account there. Repeat the field for more than one; the nth of each goes together.","type":"string"},"associated_vhid":{"description":"An entry they are part of, by VHID.","type":"string"},"associated_relation":{"description":"How they are related to it (founder, parent company, …).","type":"string"},"press_url":{"description":"Coverage of them in the press. Repeat the field for more than one; the nth of each goes together.","type":"string"},"reg_registry":{"description":"A register they are on (Companies House, …). Repeat the field for more than one; the nth of each goes together.","type":"string"},"reg_id":{"description":"Their number on it. Repeat the field for more than one; the nth of each goes together.","type":"string"},"reg_url":{"description":"Its page for them. Repeat the field for more than one; the nth of each goes together.","type":"string"},"note":{"description":"Anything else for the reviewer.","type":"string"},"consent":{"type":"string","const":"1","description":"That the information is accurate and you may share it. Required."}},"required":["title","relationship","consent"],"additionalProperties":false,"description":"An application, as the form posts it. Besides the required fields, at least one case for eligibility: a Wikipedia article, an impersonation risk, a verified account, an entry they belong to, press, or a register."},"AuthRequired":{"type":"object","properties":{"error":{"type":"string","const":"auth_required"}},"required":["error"],"additionalProperties":false,"description":"Nobody is signed in: send an API key (`Authorization: Bearer`)."},"BatchEditResult":{"anyOf":[{"type":"object","properties":{"vhid":{"type":"string"},"status":{"type":"string","const":"ok"},"revision_id":{"type":"string","pattern":"^\\d+$","description":"The live edit’s new revision."},"changed":{"type":"array","items":{"type":"string"},"description":"The fields it changed."},"handle_ops":{"description":"With `add_handles` / `remove_handles`: what each came to.","$ref":"#/components/schemas/HandleOpsReport"}},"required":["vhid","status","revision_id","changed"],"additionalProperties":false},{"type":"object","properties":{"vhid":{"type":"string"},"status":{"type":"string","const":"ok"},"submission_id":{"type":"string","pattern":"^\\d+$","description":"The proposal’s id: the one already waiting, if this one is the same."}},"required":["vhid","status","submission_id"],"additionalProperties":false},{"type":"object","properties":{"vhid":{"type":"string"},"status":{"type":"string","const":"error"},"error":{"type":"string","description":"What the single route answers for this edit: the same code (`forbidden`, `conflict`, `not_found`, a 400’s, `too_large`), or `credit_in_batch`."},"http_status":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The status the single route answers with."},"latest_revision_id":{"description":"With `conflict`: the entry’s latest revision now.","type":["string","null"]},"revisions_since_base":{"description":"With `conflict`: how many revisions were saved after `base_revision_id`.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"all_since_base_by_you":{"description":"With `conflict`: whether every one of them was saved by this account — a batch you are replaying, already applied.","type":"boolean"},"field":{"type":"string"},"platform_id":{"type":"string"},"reason":{"type":"string","enum":["unknown","disabled"]},"message":{"type":"string"},"handle_ops":{"description":"With `no_changes` from handle ops: each one a no-op.","$ref":"#/components/schemas/HandleOpsReport"}},"required":["vhid","status","error","http_status"],"additionalProperties":false},{"type":"object","properties":{"vhid":{"type":"string"},"status":{"type":"string","const":"not_applied","description":"An atomic batch was refused: this edit would have applied, and was not."}},"required":["vhid","status"],"additionalProperties":false}],"description":"What one edit of a batch came to."},"Blocked":{"type":"object","properties":{"error":{"type":"string","const":"blocked"},"query":{"type":"string","description":"The query as it was read: trimmed and cut to 100 characters."}},"required":["error","query"],"additionalProperties":false,"description":"The query contains a word the site does not search for."},"ClaimStarted":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"claimId":{"type":"string","pattern":"^\\d+$","description":"The claim’s id, to check it with."},"token":{"type":"string","description":"The token to publish where the method says."},"expires_at":{"type":"string","format":"date-time","description":"When the token stops working: a week from now."},"method":{"type":"string","enum":["url","dns","email"]},"instructions":{"type":"string","description":"Where to put the token, in words."}},"required":["ok","claimId","token","expires_at","method","instructions"],"additionalProperties":false,"description":"A claim waiting for its proof. Asking again while it waits answers the same claim and token."},"CommonsHit":{"type":"object","properties":{"filename":{"type":"string"},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"mime":{"type":["string","null"]},"token":{"type":"string","description":"The file, signed for 30 minutes: what `POST /api/media/commons` and the preview take."},"commonsPage":{"type":"string","description":"The file’s page on Commons, with its licence and author."}},"required":["filename","width","height","mime","token","commonsPage"],"additionalProperties":false,"description":"A Wikimedia Commons file a search found."},"ConnectedAppRow":{"type":"object","properties":{"key":{"type":"string","pattern":"^\\d+$","description":"The connection’s key id: disconnect it with `POST …/account/api-keys/<key>/revoke`."},"client_name":{"type":"string","description":"The name the app gave itself."},"client_host":{"type":"string","description":"Where the app’s details are published (its `client_id`’s host); for an app that registered itself, where it returns the person to (its redirect’s host, or its private-use scheme)."},"unverified":{"type":"boolean","description":"The app registered itself, so the site cannot confirm who made it."},"state":{"type":"string","enum":["live","revoked","expired"]},"role_ceiling":{"type":"string","enum":["registered","trusted_editor","admin"],"description":"The app never acts above this role."},"read_only":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["key","client_name","client_host","unverified","state","role_ceiling","read_only","created_at","expires_at","last_used_at","revoked_at"],"additionalProperties":false,"description":"One app connected to the account through OAuth (an MCP client), as its API keys page lists it."},"DescriptionSource":{"type":"object","properties":{"kind":{"type":"string","description":"The source’s kind (`wikipedia`)."},"title":{"type":"string","description":"The page’s title, underscored (`Draft:` first for a draft)."},"url":{"type":"string","description":"The page."},"adapted":{"description":"Present and true when the text has been edited on the site since.","type":"boolean"},"revision":{"description":"The page’s revision the text was taken from: its permalink is `https://en.wikipedia.org/w/index.php?title=<title>&oldid=<revision>`. Absent on a credit from before revisions were kept.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"draft":{"description":"Present and true when the page is an English Wikipedia draft (the Draft namespace), not yet an article.","type":"boolean","const":true}},"required":["kind","title","url"],"additionalProperties":false,"description":"The English Wikipedia page an entry’s description was taken from: an article, or a draft (`draft: true`)."},"DraftListItem":{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string","description":"The entry’s title as it is now."},"base_revision_id":{"anyOf":[{"type":"string","pattern":"^\\d+$"},{"type":"null"}]},"saved_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"},"available":{"type":"boolean","description":"False when the entry has since been deleted or merged away: the draft can only be discarded."}},"required":["vhid","short","title","base_revision_id","saved_at","expires_at","available"],"additionalProperties":false,"description":"One of your drafts: which entry, and when."},"EditorContext":{"type":"object","properties":{"vhid":{"type":"string"},"key":{"type":"string","description":"The entry’s canonical key: its short name, or its VHID."},"signedIn":{"type":"boolean"},"csrf":{"description":"For a browser session’s changes. A key needs none.","type":["string","null"]},"capabilities":{"type":"object","properties":{"canEditLive":{"type":"boolean","description":"May save a change live, with no review."},"canPropose":{"type":"boolean","description":"May propose a change (`POST /api/entity/{vhid}/proposals`)."},"canUploadMedia":{"type":"boolean"},"canUploadFromUrl":{"type":"boolean"},"canUploadUnlicensed":{"type":"boolean"},"canLinkImages":{"type":"boolean"}},"required":["canEditLive","canPropose","canUploadMedia","canUploadFromUrl","canUploadUnlicensed","canLinkImages"],"additionalProperties":false,"description":"What the caller may do to this entry. All false signed out."},"tools":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"Reviewers’ and managers’ tools for the entry. Null for anyone who neither reviews nor manages entries."},"proposePolicy":{"type":"string","enum":["registered","trusted","admin","closed"],"description":"Who may propose changes to it."},"editPolicy":{"type":"string","enum":["trusted","admin"],"description":"Who may edit it live."},"accountBlocked":{"type":"boolean"},"signInHost":{"type":"string","description":"The host signing in takes a visitor to (JEMEDIA Accounts), which the editor names before it does."},"draft":{"anyOf":[{"type":"object","properties":{"vhid":{"type":"string"},"base_revision_id":{"anyOf":[{"type":"string","pattern":"^\\d+$"},{"type":"null"}],"description":"The revision the draft was edited against, or null."},"saved_at":{"type":"string","format":"date-time","description":"Its last change."},"expires_at":{"type":"string","format":"date-time","description":"When it is deleted unless changed again: 30 days after `saved_at`."},"draft":{"$ref":"#/components/schemas/EntryDraft"}},"required":["vhid","base_revision_id","saved_at","expires_at","draft"],"additionalProperties":false},{"type":"null"}],"description":"Your own unsaved draft of this entry, as `GET /api/entity/{vhid}/draft` answers it, so the editor reads it here and not in a call of its own. Null when you have none, and signed out."},"baseRevisionId":{"description":"The entry’s current revision. Send it as `base_revision_id` with a change, so a change made meanwhile is caught.","type":["string","null"]},"editable":{"type":"object","properties":{"title":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"type":["string","null"]},"short":{"type":["string","null"]},"image_id":{"type":["string","null"]},"image_links":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"custom_handle_order":{"type":"boolean"}},"required":["title","subtitle","description","short","image_id","image_links","custom_handle_order"],"additionalProperties":false,"description":"The fields a change may send, as they are now."},"handles":{"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string"},"value":{"description":"Null for an email address when the caller may not edit the entry live.","type":["string","null"]},"value_norm":{"description":"Its key as the site stores it (letter case folded where the platform ignores it), to tell whether another value is the same handle. Null where `value` is masked.","type":["string","null"]},"display":{"type":["string","null"]},"masked":{"description":"Present on a masked email: the address is kept but not shown.","type":"boolean","const":true},"ref":{"description":"On a masked email: which stored handle it is (null when the entry no longer has it). Send it back as `{platform_id, value: null, ref}` to keep that address; leave it out of the list to remove it.","type":["string","null"]},"name":{"description":"The platform’s display name.","type":["string","null"]},"owner":{"type":"string","enum":["wikidata","site"],"description":"`wikidata`: the sync keeps it up to date. `site`: a person owns it."},"wd":{"anyOf":[{"type":"object","properties":{"state":{"type":"string","enum":["listed","deprecated","ended","outranked","gone"]},"since":{"type":["string","null"]},"removeAfter":{"type":["string","null"]},"replacedBy":{"type":"array","items":{"type":"string"}},"reason":{"type":["string","null"]},"qualifierOnly":{"type":"boolean","description":"A YouTube channel ID the item states only as a qualifier on its YouTube handle: `state` is `gone`, but the sync counts it as listed and never removes it."}},"required":["state","since","removeAfter","replacedBy","qualifierOnly"],"additionalProperties":false},{"type":"null"}],"description":"What the entry’s Wikidata item says about this value, or null when it says nothing."},"channel_title":{"description":"A YouTube row’s channel title, as VH knows it without asking YouTube: for a channel ID, the title VH’s handle checker last read, else the one the item’s Wikidata statement names; for a handle, its channel’s, once the checker has read it. The editor shows it under the row. Absent when there is none.","type":"string"}},"required":["platform_id","value","value_norm","display","name","owner","wd"],"additionalProperties":false},"description":"Every handle, in order, as a change sends them back."},"heldBack":{"type":"array","items":{},"description":"Values a person took off that Wikidata still lists, held back from the sync. An email’s are left out of both lists for a caller who may not edit the entry live, as its handle is masked."},"heldBackNotListed":{"type":"array","items":{},"description":"Values a person took off that Wikidata listed once and no longer lists as current. Still held back from the sync (never deleted by itself); the editor shows them collapsed, each with Allow again."},"linkNames":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"VHID → current title of each entry the description links to."},"provenance":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"For each field the sync may manage, who manages it now."},"qid":{"description":"The entry’s Wikidata item.","type":["string","null"]},"syncLocked":{"type":"boolean","description":"The sync never changes this entry (it is protected or claimed)."},"descriptionSource":{"anyOf":[{},{"type":"null"}],"description":"The \"from Wikipedia\" credit, or null."},"image":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["commons","local"]},"commons_filename":{"type":["string","null"]},"url":{"type":"string","description":"The image on this site (`/img/w/…`)."},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"commonsPage":{"type":["string","null"]},"license":{"type":["string","null"]},"license_name":{"type":["string","null"]},"credit":{"type":["string","null"]},"plate":{"description":"How the thumbnail is framed; null until the image is classified."}},"required":["id","kind","commons_filename","url","width","height","commonsPage"],"additionalProperties":false},{"type":"null"}]},"gallery":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["commons","local"]},"commons_filename":{"type":["string","null"]},"url":{"type":"string","description":"The image on this site (`/img/w/…`)."},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"commonsPage":{"type":["string","null"]},"license":{"type":["string","null"]},"license_name":{"type":["string","null"]},"credit":{"type":["string","null"]},"plate":{"description":"How the thumbnail is framed; null until the image is classified."}},"required":["id","kind","commons_filename","url","width","height","commonsPage"],"additionalProperties":false}},"galleryLogo":{"type":["string","null"]},"platforms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"category":{"type":"string"},"uri_prefix":{"type":["string","null"]},"url_template":{"type":"string","description":"How a value becomes its link (doc 01 §4)."},"is_double":{"type":"boolean"},"at_prefix":{"type":"boolean"},"wikidata_prop":{"type":["string","null"]},"case_fold":{"type":"boolean"},"icon":{"type":["string","null"]}},"required":["id","name","category","uri_prefix","url_template","is_double","at_prefix","wikidata_prop","case_fold","icon"],"additionalProperties":false},"description":"Every platform a handle may be on, the most used first."}},"required":["vhid","key","signedIn","csrf","capabilities","tools","proposePolicy","editPolicy","accountBlocked","signInHost","draft","baseRevisionId","editable","handles","heldBack","heldBackNotListed","linkNames","provenance","qid","syncLocked","descriptionSource","image","gallery","galleryLogo","platforms"],"additionalProperties":false,"description":"An entry as the editor opens it, and what the caller may do to it."},"EditPayload":{"type":"object","properties":{"title":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"description":"HTML, sanitised on the way in by the same allowlist as every description. At most 64 KiB.","type":["string","null"]},"description_source":{"description":"Only `null`, which takes the \"from Wikipedia\" credit off. A credit is never sent: name its page with `wikipedia_credit`.","type":"null"},"wikipedia_credit":{"description":"Credit the description “from Wikipedia”. The server reads the page itself (redirects followed, so a draft since moved is its article), and refuses it unless at least half of the description’s three-word phrases are the page’s lead (`409 credit_text_differs`, with `page` and `share`). Not with `description_source: null`. Errors: `400 bad_wikipedia_title` / `not_english_wikipedia`; `409 wikipedia_page_missing`, `wikipedia_namespace` (not an article or a draft), `wikipedia_section`, `wikipedia_no_lead`, `no_description`, `draft_has_article` (the entry’s item already has an English article, named in `article`: credit that); `502 wikipedia_unreachable`.","type":"object","properties":{"page":{"type":"string","description":"An English Wikipedia article or draft, by title (`Draft:Dominic McLaughlin`) or address (`https://en.wikipedia.org/wiki/…`)."},"let_wikidata_manage":{"description":"For a draft only: `false` keeps the description site-authored. By default a draft’s credit hands the description to the Wikidata sync, which leaves it alone until the draft is an article linked from the entry’s Wikidata item.","type":"boolean"}},"required":["page"],"additionalProperties":false},"short":{"description":"The short name (`/<short>`); `null` removes it.","type":["string","null"]},"image_id":{"description":"The lead image, by media id; `null` removes it.","type":["string","null"]},"gallery":{"description":"The whole gallery after the lead, in order, by media id: it replaces the one there.","type":"array","items":{"type":"string"}},"image_links":{"description":"The whole map of links on the entry’s images, media id → absolute http(s) address. Needs the `link_images` capability.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"custom_handle_order":{"description":"Keep `handles` in the order given, rather than the site’s order.","type":"boolean"},"handles":{"description":"The whole list of handles, in order: it replaces the one there. Not with `add_handles` / `remove_handles`.","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string"},"value":{"type":"string"},"display":{"description":"A label to show instead of the value.","type":["string","null"]}},"required":["platform_id","value"],"additionalProperties":false}},"add_handles":{"description":"Handles to put on the entry, at the end of its list as it is when the change is applied (a proposal: when it is approved). One the entry has already (the same value, in any case the platform folds) changes nothing. No base revision is needed for it. Not with `handles` (400 `handles_with_ops`).","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string"},"value":{"type":"string","description":"Not blank. A website is completed to an address, as in `handles`."},"display":{"description":"A label to show instead of the value.","type":["string","null"]}},"required":["platform_id","value"],"additionalProperties":false}},"remove_handles":{"description":"Handles to take off, by value — or, for an email the caller may not see, by `ref` (an address is refused: `masked_handle_needs_ref`). One the entry does not have changes nothing. A value the Wikidata sync supplied is held back from it, as when a list leaves it out. Not with `handles`.","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"description":"The handle, as stored or in any case the platform folds.","type":"string"},"ref":{"description":"Instead of `value`, for an email the caller may not see: the `ref` `GET /api/editor/{vhid}` gives it.","type":"string"}},"required":["platform_id"],"additionalProperties":false}},"relinquish":{"description":"Fields to hand back to the Wikidata sync.","type":"array","items":{"type":"string"}},"takeover":{"description":"Fields to take from the Wikidata sync, keeping their value.","type":"array","items":{"type":"string"}},"handle_takeover":{"description":"Handles to take from the Wikidata sync.","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"type":"string","description":"The handle as stored."}},"required":["platform_id","value"],"additionalProperties":false}},"handle_relinquish":{"description":"Handles to hand to the Wikidata sync, while Wikidata lists them.","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"type":"string","description":"The handle as stored."}},"required":["platform_id","value"],"additionalProperties":false}},"handle_release":{"description":"Values held back from the sync that it may add again.","type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"type":"string","description":"The handle as stored."}},"required":["platform_id","value"],"additionalProperties":false}}},"additionalProperties":false,"description":"A change to an entry, as the editor sends it. Every field is optional: send only what changes. `handles` and `gallery` are whole lists, which replace the entry’s; `add_handles` / `remove_handles` name single handles instead, applied to the list as it is then, so a change of nothing else needs no base revision and never undoes a handle saved meanwhile. A field the server does not know is ignored."},"EmptySearch":{"type":"object","properties":{"query":{"type":"string","const":""},"total":{"type":"number","const":0},"results":{"type":"array","prefixItems":[],"items":false,"maxItems":0}},"required":["query","total","results"],"additionalProperties":false,"description":"An empty `q`: nothing is searched."},"Entity":{"type":"object","properties":{"vhid":{"type":"string","pattern":"^\\d{11}$","description":"The entry’s VHID: 11 digits, permanent."},"short":{"description":"The entry’s short name (`/<short>`), if it has one.","type":["string","null"]},"title":{"type":"string"},"subtitle":{"type":["string","null"]},"entity_type":{"description":"What it is, from its Wikidata item: `person`, `organization` or `brand`; null when unknown. `entity_kind` says more.","type":["string","null"]},"entity_kind":{"anyOf":[{"type":"string","enum":["person","musical_group","sports_organization","broadcaster","publisher","educational_institution","research_organization","religious_organization","museum","financial_institution","chain","nonprofit","company","organization","show","website","software","brand"]},{"type":"null"}],"description":"The finer kind of thing it is, from its Wikidata item’s classes; null when unknown (an organisation or a brand not yet read since kinds were added reads null until the sync next reads its item). Each kind belongs to one `entity_type`, and `person`, `organization` and `brand` are also the kinds of an entry of that type with no finer one. The kinds, with their type: `person` (person: person), `musical_group` (organization: musical group), `sports_organization` (organization: sports organisation), `broadcaster` (organization: broadcaster), `publisher` (organization: publisher), `educational_institution` (organization: educational institution), `research_organization` (organization: research organisation), `religious_organization` (organization: religious organisation), `museum` (organization: museum), `financial_institution` (organization: financial institution), `chain` (organization: retail or hospitality chain), `nonprofit` (organization: charity or non-profit), `company` (organization: company), `organization` (organization: organisation), `show` (brand: show), `website` (brand: website or app), `software` (brand: software), `brand` (brand: brand). New kinds may be added; treat an unknown value as its `entity_type`."},"verified":{"type":"boolean","description":"Verified Handles has verified the entry."},"description":{"description":"The description as sanitised HTML, links to other entries named as they are today.","type":["string","null"]},"description_source":{"anyOf":[{"$ref":"#/components/schemas/DescriptionSource"},{"type":"null"}]},"description_license":{"anyOf":[{"type":"string","const":"CC-BY-SA-4.0"},{"type":"null"}],"description":"The description’s licence, as an SPDX id: `CC-BY-SA-4.0` when it was taken from Wikipedia (`description_source` is set), which means crediting the article and sharing alike. Null otherwise: the description is CC0 like the rest of the text."},"url":{"type":"string","description":"The entry’s canonical page."},"image":{"description":"The lead image (the same as `images[0].url`), or null.","type":["string","null"]},"image_rights":{"anyOf":[{"$ref":"#/components/schemas/ImageRights"},{"type":"null"}],"description":"The lead image’s rights, or null when there is no image."},"images":{"type":"array","items":{"$ref":"#/components/schemas/EntityImage"},"description":"Every image the entry shows, the lead first. Empty when it shows none."},"handles":{"type":"array","items":{"$ref":"#/components/schemas/Handle"}}},"required":["vhid","short","title","subtitle","entity_type","entity_kind","verified","description","description_source","description_license","url","image","image_rights","images","handles"],"additionalProperties":false,"description":"An entry: the same data its page shows, from the same code. Email addresses are masked. The text is CC0, except a description taken from Wikipedia (`description_license`)."},"EntityBatch":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/EntityBatchResult"},"description":"One element per key asked for, in the order asked, a repeated key once (where it first appears)."}},"required":["results"],"additionalProperties":false,"description":"Many entries at once: each key’s answer, in request order."},"EntityBatchResult":{"oneOf":[{"type":"object","properties":{"key":{"type":"string","description":"The key as asked for, trimmed."},"status":{"type":"string","const":"ok"},"entity":{"description":"Exactly what `GET /api/v1/entities/{key}` answers for this key.","$ref":"#/components/schemas/Entity"}},"required":["key","status","entity"],"additionalProperties":false},{"type":"object","properties":{"key":{"type":"string","description":"The key as asked for, trimmed."},"status":{"type":"string","const":"not_found","description":"No public entry answers to the key: where the single read answers 404."}},"required":["key","status"],"additionalProperties":false}],"description":"What one requested key answers."},"EntityHandles":{"type":"object","properties":{"vhid":{"type":"string","pattern":"^\\d{11}$","description":"The entry’s VHID: 11 digits, permanent."},"handles":{"type":"array","items":{"$ref":"#/components/schemas/Handle"}}},"required":["vhid","handles"],"additionalProperties":false,"description":"An entry’s handles alone: the same list as the entry’s `handles`."},"EntityHistory":{"type":"object","properties":{"vhid":{"type":"string","pattern":"^\\d{11}$","description":"The entry’s VHID: 11 digits, permanent."},"merged_into":{"description":"Present when the entry was merged into another: that entry’s VHID. The history is still the merged entry’s own.","type":"string","pattern":"^\\d{11}$"},"limit":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"The page size used: the `limit` asked for, clamped to the endpoint’s range."},"offset":{"type":"integer","minimum":0,"maximum":10000,"description":"The offset used: the `offset` asked for, clamped to 0–10,000."},"next_offset":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}],"description":"The `offset` of the next page, or null when this is the last one. Follow it rather than computing it."},"history":{"type":"array","items":{"$ref":"#/components/schemas/Revision"},"description":"Newest first."}},"required":["vhid","limit","offset","next_offset","history"],"additionalProperties":false,"description":"A page of an entry’s public revision history, newest first."},"EntityHistoryPage":{"type":"object","properties":{"vhid":{"type":"string","pattern":"^\\d{11}$","description":"The entry’s VHID: 11 digits, permanent."},"key":{"type":"string","description":"The entry’s canonical key: its short name, or its VHID when it has none."},"merged_into":{"description":"Present when the entry was merged into another: that entry’s VHID. The history is still the merged entry’s own.","type":"string","pattern":"^\\d{11}$"},"page":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"The page, 50 revisions each."},"hasMore":{"type":"boolean","description":"There is a next page (`?p=` one more)."},"history":{"type":"array","items":{"$ref":"#/components/schemas/Revision"},"description":"Newest first."}},"required":["vhid","key","page","hasMore","history"],"additionalProperties":false,"description":"A page of an entry’s public history, as its history page shows it: 50 a page, by page number."},"EntityImage":{"type":"object","properties":{"url":{"type":"string","description":"The image, as a path on this site (`/img/w/480q75/<id>`)."},"main":{"type":"boolean","description":"True for the lead image, false for the gallery images after it."},"image_rights":{"$ref":"#/components/schemas/ImageRights"},"link":{"description":"Where the image links on the entry page instead of opening the full-screen viewer, or null.","type":["string","null"]}},"required":["url","main","image_rights","link"],"additionalProperties":false,"description":"One image an entry shows."},"EntryDraft":{"type":"object","properties":{"vhid":{"description":"The entry’s VHID. Optional when sending; it must be this entry’s.","type":"string"},"savedAt":{"description":"When the draft was last changed, in milliseconds since 1970. Ignored when sending: the server keeps its own time.","type":"number"},"fields":{"type":"object","properties":{"title":{"type":"string"},"subtitle":{"type":"string"},"description":{"type":"string"},"short":{"type":"string"}},"additionalProperties":false,"description":"The entry’s fields as edited. The description is HTML, kept through the same allowlist as a saved description."},"handles":{"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string"},"value":{"type":"string"},"display":{"type":["string","null"]},"kept":{"description":"A masked email kept as stored.","type":"boolean","const":true},"ref":{"description":"Which stored email a kept one is.","type":"string"}},"required":["platform_id","value"],"additionalProperties":false}},"customOrder":{"description":"The handles are in the entry’s own order.","type":"boolean"},"linkNames":{"description":"VHID → name, for the entries the description links to.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"image":{"description":"The lead image as picked: null when removed, absent when untouched.","anyOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{1,19}$","description":"The image’s id."},"kind":{"type":"string","enum":["commons","local"]},"commons_filename":{"type":["string","null"]}},"required":["id","kind","commons_filename"],"additionalProperties":false},{"type":"null"}]},"gallery":{"description":"The images after the lead, when changed.","maxItems":5,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d{1,19}$","description":"The image’s id."},"kind":{"type":"string","enum":["commons","local"]},"commons_filename":{"type":["string","null"]}},"required":["id","kind","commons_filename"],"additionalProperties":false}},"imageLinks":{"description":"Image id → link as typed, when changed.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"relinquish":{"description":"Fields to hand to Wikidata on save.","type":"array","items":{"type":"string","enum":["title","subtitle","description","image"]}},"takeover":{"description":"Fields to take back from Wikidata on save.","type":"array","items":{"type":"string","enum":["title","subtitle","description","image"]}},"dropCredit":{"description":"Remove the Wikipedia credit on save.","type":"boolean","const":true},"creditPage":{"description":"A Wikipedia page named for the credit, as typed.","type":"string"},"creditManage":{"description":"With `creditPage`: the description is not to go to the sync.","type":"boolean","const":false},"handleTakeover":{"type":"array","items":{"type":"string"}},"handleRelinquish":{"type":"array","items":{"type":"string"}},"handleRelease":{"type":"array","items":{"type":"string"}}},"required":["fields"],"additionalProperties":false,"description":"An unsaved edit to an entry, in the editor’s own shape."},"Forbidden":{"type":"object","properties":{"error":{"type":"string","const":"forbidden"}},"required":["error"],"additionalProperties":false,"description":"The key’s account may not do this: it lacks the capability, or the account is blocked."},"Freshness":{"type":"object","properties":{"updated_at":{"type":"string","format":"date-time","description":"The entry’s last change."}},"required":["updated_at"],"additionalProperties":false,"description":"When an entry last changed."},"Handle":{"type":"object","properties":{"platform":{"type":"string","description":"The platform’s id (`twitter`, `instagram`, `wikidata`, …)."},"name":{"type":"string","description":"The platform’s display name."},"category":{"type":"string","enum":["social","messaging","website","identifier"],"description":"How the entry page groups it."},"value":{"description":"The handle as stored (a username, an id, a URL). Null for an email address, which is never published as data.","type":["string","null"]},"display":{"description":"The label the entry page shows instead of the value, if an editor set one. Null for email.","type":["string","null"]},"url":{"description":"The profile’s address, built from the platform’s URL pattern. Null for email.","type":["string","null"]},"verified":{"type":"boolean","description":"Verified Handles has checked that this account belongs to the entry."},"checked":{"description":"What Verified Handles’ own handle checker found when it last looked at this account, from the platform’s official API: only on YouTube (channel and handle), Deezer and GitHub, and only for a look at most 7 days old. Absent otherwise. The entry page shows it only for `dead` (“Not found when checked …”) and `renamed`.","type":"object","properties":{"verdict":{"type":"string","enum":["live","dead","renamed","unknown"],"description":"`live`: the profile was there. `dead`: the platform did not find it. `renamed`: it answers under another name. `unknown`: the platform gave no answer a rule could read."},"at":{"type":"string","description":"When it looked (ISO 8601, UTC)."},"now":{"description":"For `renamed`, the name it answers to now, when the platform gave one. A YouTube handle’s is lowercase.","type":"string"}},"required":["verdict","at"],"additionalProperties":false}},"required":["platform","name","category","value","display","url","verified"],"additionalProperties":false,"description":"One account or identifier of an entry, in the order the entry page shows them. A Wikidata item linked to the entry comes last, as platform `wikidata`."},"HandleHolder":{"type":"object","properties":{"vhid":{"type":"string","description":"The entry’s VHID."},"title":{"type":"string","description":"The entry’s title."},"url":{"type":"string","description":"The entry’s page."},"value":{"type":"string","description":"The handle as the entry lists it: on a platform that ignores case, its case may differ from the value asked."},"verified":{"type":"boolean","description":"Whether the handle itself is verified on this entry (not the entry’s own badge)."}},"required":["vhid","title","url","value","verified"],"additionalProperties":false,"description":"An entry that lists the handle."},"HandleLookupResponse":{"type":"object","properties":{"platform":{"type":"string","description":"The platform looked on: the one asked, or the one whose address `url` is."},"value":{"type":"string","description":"The handle looked up, as the site stores one: from `url`, what follows the platform’s address."},"results":{"type":"array","items":{"$ref":"#/components/schemas/HandleHolder"},"description":"Verified handles first, then by VHID; at most 50. Empty when no public entry lists it."}},"required":["platform","value","results"],"additionalProperties":false,"description":"The public entries that list a handle."},"HandleOpsReport":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"type":"string","description":"The handle as stored."}},"required":["platform_id","value"],"additionalProperties":false},"description":"Put on the entry, at the end of its list."},"removed":{"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"description":"The handle, as stored or in any case the platform folds.","type":"string"},"ref":{"description":"Instead of `value`, for an email the caller may not see: the `ref` `GET /api/editor/{vhid}` gives it.","type":"string"}},"required":["platform_id"],"additionalProperties":false},"description":"Taken off, as the op named them."},"already_present":{"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"type":"string","description":"The handle as stored."}},"required":["platform_id","value"],"additionalProperties":false},"description":"Adds of a handle the entry has already: nothing changed."},"not_present":{"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string","description":"The platform’s id."},"value":{"description":"The handle, as stored or in any case the platform folds.","type":"string"},"ref":{"description":"Instead of `value`, for an email the caller may not see: the `ref` `GET /api/editor/{vhid}` gives it.","type":"string"}},"required":["platform_id"],"additionalProperties":false},"description":"Removes of a handle the entry does not have: nothing changed."}},"required":["added","removed","already_present","not_present"],"additionalProperties":false,"description":"What each of a change’s `add_handles` / `remove_handles` came to."},"HistoryHidden":{"type":"object","properties":{"error":{"type":"string","const":"history_hidden"},"message":{"type":"string","description":"What the history page says: the history isn’t public, and how to ask for it."},"contact":{"type":"string","description":"The contact page, where a request for the history goes."},"subject":{"type":"string","description":"The subject to give the request, naming the entry: `History request: <title> (<vhid>)`."}},"required":["error","message","contact","subject"],"additionalProperties":false,"description":"The entry is public but its history has been hidden."},"ImageRights":{"type":"object","properties":{"source":{"type":"string","enum":["wikimedia_commons","upload"],"description":"Where the image came from."},"license":{"anyOf":[{"type":"string","enum":["cc0-1.0","public-domain","cc-by-4.0","cc-by-sa-4.0","supplied","permission","none"]},{"type":"null"}],"description":"An upload’s licence, by id. Null for a Commons image (see `license_name`) or when none was recorded."},"license_name":{"description":"The licence’s name, e.g. \"CC BY-SA 4.0\". Null until it is known.","type":["string","null"]},"license_url":{"description":"The licence’s text.","type":["string","null"]},"freely_licensed":{"description":"May be reused under a free licence. Null when no licence is recorded.","type":["boolean","null"]},"credit":{"description":"The credit line to show when reusing the image.","type":["string","null"]},"source_page":{"description":"The image’s Wikimedia Commons file page, which states its licence and author.","type":["string","null"]},"details_url":{"type":"string","description":"The entry’s \"About this image\" page."}},"required":["source","license","license_name","license_url","freely_licensed","credit","source_page","details_url"],"additionalProperties":false,"description":"Who owns an image and how it may be reused. An entry’s text is CC0 (bar a description taken from Wikipedia); its images are licensed separately, by their owners."},"KeyActivityRow":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$","description":"The audit row’s id: pass it as `before` or `after` to page."},"ts":{"type":"string","format":"date-time"},"label":{"type":"string","description":"The action in words."},"sentence":{"type":"string","description":"The row in one sentence, with names rather than ids."},"surface":{"description":"What it came through: `api` (the key itself) or `mcp` (an MCP tool with it).","type":["string","null"]},"mcp_tool":{"description":"The MCP tool that made it, if one did.","type":["string","null"]},"ip":{"description":"The IP address it came from, only where the row is your own act; erased a year after it was recorded.","type":["string","null"]},"client":{"description":"The client in plain words (“curl”, “Claude Code”), from its user agent.","type":["string","null"]}},"required":["id","ts","label","sentence","surface","mcp_tool","ip","client"],"additionalProperties":false,"description":"One change made with one of your API keys, as its activity page shows it."},"LibraryImage":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$","description":"The image’s id: what a proposal names as `image_id`."},"kind":{"type":"string","enum":["commons","local"],"description":"`commons`: a Wikimedia Commons file; `local`: an upload."},"filename":{"description":"A Commons file’s name; null for an upload.","type":["string","null"]},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"license":{"description":"An upload’s licence id; null for a Commons file.","type":["string","null"]},"license_name":{"type":["string","null"]},"license_url":{"type":["string","null"]},"credit":{"type":["string","null"]},"commons_page":{"type":["string","null"]},"used_by":{"type":"array","items":{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string"}},"required":["vhid","short","title"],"additionalProperties":false},"description":"Entries showing it now."},"used_before":{"type":"array","items":{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string"}},"required":["vhid","short","title"],"additionalProperties":false},"description":"Entries that showed it before."},"restricted":{"description":"Present only to an account that may use restricted images.","type":"boolean","const":true}},"required":["id","kind","filename","width","height","license","license_name","license_url","credit","commons_page","used_by","used_before"],"additionalProperties":false,"description":"An image the site holds, as the picker lists it."},"LinkSearch":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"vhid":{"type":"string"},"title":{"type":"string"},"subtitle":{"type":["string","null"]},"short":{"type":["string","null"]}},"required":["vhid","title","subtitle","short"],"additionalProperties":false}}},"required":["results"],"additionalProperties":false,"description":"Entries to link to."},"NotAccepting":{"type":"object","properties":{"error":{"type":"string","const":"not_accepting"},"message":{"type":"string","description":"The sentence to show the person."}},"required":["error","message"],"additionalProperties":false,"description":"New entries are paused: nothing new is queued. What is queued is still reviewed."},"NotFound":{"type":"object","properties":{"error":{"type":"string","const":"not_found"}},"required":["error"],"additionalProperties":false,"description":"Nothing public answers to that key, or the part asked for is not public."},"NotFoundKey":{"type":"object","properties":{"error":{"type":"string","const":"not_found"},"key":{"type":"string","description":"The key as asked for, without `.json`."}},"required":["error","key"],"additionalProperties":false,"description":"No public entry answers to that key. A JSON client is never redirected to a guess."},"OAuthAuthorizationServer":{"type":"object","properties":{"issuer":{"type":"string","description":"The site’s origin. The `iss` on every authorization response is this."},"authorization_endpoint":{"type":"string","description":"A page for the person, in a browser: they sign in and choose what the app may do."},"token_endpoint":{"type":"string"},"revocation_endpoint":{"type":"string"},"registration_endpoint":{"type":"string","description":"Dynamic Client Registration (RFC 7591), for a client that has no Client ID Metadata Document. The person sees such an app marked unverified."},"response_types_supported":{"type":"array","prefixItems":[{"type":"string","const":"code"}],"items":false,"minItems":1,"maxItems":1},"grant_types_supported":{"type":"array","prefixItems":[{"type":"string","const":"authorization_code"},{"type":"string","const":"refresh_token"}],"items":false,"minItems":2,"maxItems":2},"code_challenge_methods_supported":{"type":"array","prefixItems":[{"type":"string","const":"S256"}],"items":false,"minItems":1,"maxItems":1,"description":"PKCE is required, with S256."},"token_endpoint_auth_methods_supported":{"type":"array","prefixItems":[{"type":"string","const":"none"}],"items":false,"minItems":1,"maxItems":1,"description":"Public clients only: no client secret."},"revocation_endpoint_auth_methods_supported":{"type":"array","prefixItems":[{"type":"string","const":"none"}],"items":false,"minItems":1,"maxItems":1},"scopes_supported":{"type":"array","prefixItems":[{"type":"string","const":"mcp"}],"items":false,"minItems":1,"maxItems":1},"client_id_metadata_document_supported":{"type":"boolean","const":true,"description":"A client’s `client_id` may be the https URL of its Client ID Metadata Document, fetched when a person connects it."},"authorization_response_iss_parameter_supported":{"type":"boolean","const":true},"service_documentation":{"type":"string"}},"required":["issuer","authorization_endpoint","token_endpoint","revocation_endpoint","registration_endpoint","response_types_supported","grant_types_supported","code_challenge_methods_supported","token_endpoint_auth_methods_supported","revocation_endpoint_auth_methods_supported","scopes_supported","client_id_metadata_document_supported","authorization_response_iss_parameter_supported","service_documentation"],"additionalProperties":false,"description":"RFC 8414 metadata for the site’s authorization server."},"OAuthError":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_request","invalid_client","invalid_grant","unsupported_grant_type","invalid_target","temporarily_unavailable"],"description":"The RFC 6749 §5.2 error code."},"error_description":{"type":"string","description":"What was wrong, in words. It never repeats a code, token or verifier."}},"required":["error","error_description"],"additionalProperties":false,"description":"An OAuth token or revocation request the server refused (RFC 6749 §5.2)."},"OAuthProtectedResource":{"type":"object","properties":{"resource":{"type":"string","description":"The MCP address this document is about: a token is valid there and nowhere else."},"authorization_servers":{"minItems":1,"maxItems":1,"type":"array","items":{"type":"string"},"description":"The issuer: the site itself."},"scopes_supported":{"type":"array","prefixItems":[{"type":"string","const":"mcp"}],"items":false,"minItems":1,"maxItems":1},"bearer_methods_supported":{"type":"array","prefixItems":[{"type":"string","const":"header"}],"items":false,"minItems":1,"maxItems":1,"description":"A token goes in `Authorization: Bearer`, never in a query string or a form."},"resource_name":{"type":"string"},"resource_documentation":{"type":"string"}},"required":["resource","authorization_servers","scopes_supported","bearer_methods_supported","resource_name","resource_documentation"],"additionalProperties":false,"description":"RFC 9728 metadata for one MCP address."},"OAuthRegisteredClient":{"type":"object","properties":{"client_id":{"type":"string","pattern":"^vhdcr_[A-Za-z0-9_-]{22}$","description":"The client’s id, for the authorization, token and revocation requests. Not a secret."},"client_id_issued_at":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"When it was issued, in seconds since 1970."},"client_name":{"type":"string","description":"The name, as kept: control characters dropped, trimmed."},"redirect_uris":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"string"},"description":"The redirect URIs kept: those the server would never send a code to are dropped."},"grant_types":{"type":"array","prefixItems":[{"type":"string","const":"authorization_code"},{"type":"string","const":"refresh_token"}],"items":false,"minItems":2,"maxItems":2},"response_types":{"type":"array","prefixItems":[{"type":"string","const":"code"}],"items":false,"minItems":1,"maxItems":1},"token_endpoint_auth_method":{"type":"string","const":"none"}},"required":["client_id","client_id_issued_at","client_name","redirect_uris","grant_types","response_types","token_endpoint_auth_method"],"additionalProperties":false,"description":"A registered client (RFC 7591 §3.2.1). There is no client secret."},"OAuthRegisterError":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_client_metadata","invalid_redirect_uri","temporarily_unavailable"],"description":"The RFC 7591 §3.2.2 error code."},"error_description":{"type":"string","description":"What was wrong, in words."}},"required":["error","error_description"],"additionalProperties":false,"description":"A client registration the server refused (RFC 7591 §3.2.2)."},"OAuthTokens":{"type":"object","properties":{"access_token":{"type":"string","pattern":"^vho_[0-9a-f]{12}_[A-Za-z0-9_-]{43}$","description":"Send it as `Authorization: Bearer`, to the MCP address it was issued for."},"token_type":{"type":"string","const":"Bearer"},"expires_in":{"type":"integer","minimum":1,"maximum":3600,"description":"Seconds the access token works: an hour, or less when the connection ends sooner."},"refresh_token":{"type":"string","pattern":"^vhr_[0-9a-f]{12}_[A-Za-z0-9_-]{43}$","description":"Use it once, for the next pair. Presenting an old one again ends the connection."},"scope":{"type":"string","const":"mcp"}},"required":["access_token","token_type","expires_in","refresh_token","scope"],"additionalProperties":false,"description":"A new access and refresh token for one connection."},"OffsetPagination":{"type":"object","properties":{"limit":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"The page size used: the `limit` asked for, clamped to the endpoint’s range."},"offset":{"type":"integer","minimum":0,"maximum":10000,"description":"The offset used: the `offset` asked for, clamped to 0–10,000."},"next_offset":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}],"description":"The `offset` of the next page, or null when this is the last one. Follow it rather than computing it."}},"required":["limit","offset","next_offset"],"additionalProperties":false,"description":"How the paged lists page: ask with `limit` and `offset`, follow `next_offset` until it is null. The window stops at an offset of 10,000; past it the offset is clamped, silently."},"OpenApiDocument":{"type":"object","properties":{"openapi":{"type":"string","const":"3.1.0"},"info":{"type":"object","properties":{"title":{"type":"string"},"version":{"type":"string"}},"required":["title","version"],"additionalProperties":{}},"paths":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"components":{"type":"object","properties":{},"additionalProperties":{}}},"required":["openapi","info","paths","components"],"additionalProperties":{},"description":"An OpenAPI 3.1 document: this one."},"Prefs":{"type":"object","properties":{"signedIn":{"type":"boolean","description":"A key or a session was sent and is good."},"csrf":{"description":"The token a browser session sends back as `X-CSRF-Token` on a change. A key needs none. Null signed out.","type":["string","null"]},"copyHandleOnly":{"description":"Copy a handle without its `@` or address. Null signed out.","type":["boolean","null"]},"saveRecentSearches":{"description":"Keep recent searches with the account. Null signed out.","type":["boolean","null"]}},"required":["signedIn","csrf","copyHandleOnly","saveRecentSearches"],"additionalProperties":false,"description":"The caller’s preferences, or nulls when nobody is signed in."},"PrefsChange":{"type":"object","properties":{"copyHandleOnly":{"type":"boolean"},"emailNotifications":{"description":"Email me when something I sent is decided.","type":"boolean"},"nameVisibility":{"description":"Who sees my name on what I do: everyone, trusted editors and admins, or admins only.","type":"string","enum":["public","trusted","admins"]},"saveRecentSearches":{"type":"boolean"}},"additionalProperties":false,"description":"The preferences to change: at least one; the rest are left as they are."},"ProposalQueued":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"submissionId":{"type":"string","pattern":"^\\d+$","description":"The proposal’s id."},"status":{"type":"string","const":"pending","description":"It waits for a reviewer."}},"required":["ok","submissionId","status"],"additionalProperties":false,"description":"The proposal is queued for a reviewer. Sending the same proposal again while it waits answers the same id rather than queuing it twice."},"Revision":{"type":"object","properties":{"ref_id":{"type":"string","pattern":"^[ar]\\d+$","description":"The revision’s id: `a<n>` for the history kept from the old wiki, `r<n>` for an edit made since."},"kind":{"type":"string","enum":["archive","forward"],"description":"`archive`: from the old wiki. `forward`: made on Verified Handles."},"ts":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When it was made."},"actor":{"description":"Who made it, as they chose to be named in public; null when they keep their name private.","type":["string","null"]},"summary":{"description":"The edit summary.","type":["string","null"]},"is_bot":{"description":"Made by an automated process (the Wikidata sync, an import).","type":["boolean","null"]},"source":{"type":"string","enum":["import","mediawiki","sync","admin","trusted","submission","claim"],"description":"How the edit was made."},"hidden":{"type":"boolean","const":false,"description":"Always false here: a hidden revision is never in the public history."},"deleted":{"type":"boolean","const":false,"description":"Always false here: a deleted revision is never in the public history."},"proposer":{"description":"For an approved proposal, who proposed it, as they chose to be named; null otherwise.","type":["string","null"]}},"required":["ref_id","kind","ts","actor","summary","is_bot","source","hidden","deleted","proposer"],"additionalProperties":false,"description":"One revision in an entry’s public history."},"RevisionState":{"type":"object","properties":{"vhid":{"type":"string"},"revision":{"type":"object","properties":{"ref_id":{"type":"string","pattern":"^r\\d+$"},"ts":{"type":"string","format":"date-time"},"summary":{"type":["string","null"]},"hidden":{"type":"boolean"}},"required":["ref_id","ts","summary","hidden"],"additionalProperties":false},"fields":{"type":"object","properties":{"title":{"type":"string"},"subtitle":{"type":["string","null"]},"description":{"type":["string","null"]},"short":{"type":["string","null"]},"image_id":{"type":["string","null"]},"gallery":{"type":"array","items":{"type":"string"}},"image_links":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"custom_handle_order":{"type":"boolean"}},"required":["title","subtitle","description","short","image_id","gallery","image_links","custom_handle_order"],"additionalProperties":false,"description":"The fields as they were."},"handles":{"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string"},"value":{"description":"Null for an email address when the caller may not edit the entry live.","type":["string","null"]},"display":{"type":["string","null"]},"masked":{"description":"Present on a masked email: the address is kept but not shown.","type":"boolean","const":true},"ref":{"description":"On a masked email: which stored handle it is (null when the entry no longer has it). Send it back as `{platform_id, value: null, ref}` to keep that address; leave it out of the list to remove it.","type":["string","null"]}},"required":["platform_id","value","display"],"additionalProperties":false},"description":"The handles as they were, in order."},"unavailable_images":{"type":"array","items":{"type":"string"},"description":"Images it showed that may not be proposed again (withdrawn or gone since)."}},"required":["vhid","revision","fields","handles","unavailable_images"],"additionalProperties":false,"description":"An entry as it was at one revision, as a revert would put it back."},"SearchResponse":{"type":"object","properties":{"query":{"type":"string","description":"The query as it was read: trimmed and cut to 100 characters."},"total":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many entries matched in all."},"limit":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"The page size used: the `limit` asked for, clamped to the endpoint’s range."},"offset":{"type":"integer","minimum":0,"maximum":10000,"description":"The offset used: the `offset` asked for, clamped to 0–10,000."},"next_offset":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}],"description":"The `offset` of the next page, or null when this is the last one. Follow it rather than computing it."},"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"},"description":"Best match first."}},"required":["query","total","limit","offset","next_offset","results"],"additionalProperties":false,"description":"A page of search results."},"SearchResult":{"type":"object","properties":{"vhid":{"type":"string","pattern":"^\\d{11}$","description":"The entry’s VHID: 11 digits, permanent."},"short":{"description":"The entry’s short name (`/<short>`), if it has one.","type":["string","null"]},"title":{"type":"string"},"subtitle":{"type":["string","null"]},"verified":{"type":"boolean"},"snippet":{"description":"Plain text from the description around the words that matched, or null when the match was not in it.","type":["string","null"]},"highlighted":{"type":"boolean","description":"`snippet` is a real excerpt (true), or there is none (false)."},"url":{"type":"string","description":"The entry’s page."}},"required":["vhid","short","title","subtitle","verified","snippet","highlighted","url"],"additionalProperties":false,"description":"One entry that matched."},"SubmissionBrief":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"kind":{"type":"string","enum":["new_entity","correction","claim","report","application"],"description":"What was asked: a new entry, a change, a claim, a report or an application."},"status":{"type":"string","enum":["pending","needs_info","approved","rejected","merged","spam","superseded","retracted","resolved"]},"entry":{"anyOf":[{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string"}},"required":["vhid","short","title"],"additionalProperties":false},{"type":"null"}],"description":"The entry it is about; null for a new entry or an application."},"qid":{"description":"The Wikidata item it names, if any.","type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"review_notes":{"description":"What the reviewer wrote when deciding it.","type":["string","null"]}},"required":["id","kind","status","entry","qid","created_at","decided_at","review_notes"],"additionalProperties":false,"description":"One thing you sent for review, as your submissions list shows it."},"SubmissionDetail":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"kind":{"type":"string","enum":["new_entity","correction","claim","report","application"],"description":"What was asked: a new entry, a change, a claim, a report or an application."},"status":{"type":"string","enum":["pending","needs_info","approved","rejected","merged","spam","superseded","retracted","resolved"]},"entry":{"anyOf":[{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string"}},"required":["vhid","short","title"],"additionalProperties":false},{"type":"null"}],"description":"The entry it is about; null for a new entry or an application."},"qid":{"description":"The Wikidata item it names, if any.","type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"review_notes":{"description":"What the reviewer wrote when deciding it.","type":["string","null"]},"mine":{"type":"boolean","description":"You sent it."},"payload":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"What it asks for, in its kind’s shape: for `correction`, `new_entity` and `application`, an `EditPayload` (the fields to set, and for a revert or an approval the server’s own); for `report`, `{reason, note?, media_id?, link?}`; for `claim`, `{method}`."},"comments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^\\d+$"},"author_name":{"description":"The writer’s name, where you may see it.","type":["string","null"]},"author_role":{"description":"`reviewer` for a reviewer’s comment; null for the submitter’s own.","type":["string","null"]},"body":{"type":"string"},"created_at":{"type":"string","format":"date-time"}},"required":["id","author_name","author_role","body","created_at"],"additionalProperties":false}}},"required":["id","kind","status","entry","qid","created_at","decided_at","review_notes","mine","payload","comments"],"additionalProperties":false,"description":"One submission, what it asked for, and its conversation."},"TypeaheadRow":{"type":"object","properties":{"VHID":{"type":"string"},"Title":{"type":"string"},"Subtitle":{"type":["string","null"]},"short":{"type":["string","null"]},"V":{"type":"string","enum":["0","1"],"description":"`1`: the entry is verified."},"S":{"type":"string","pattern":"^\\d+$","description":"How often the entry has been in the spotlight, as digits."},"T":{"type":"string","enum":["1",""],"description":"`1`: the entry is trending now."}},"required":["VHID","Title","Subtitle","short","V","S","T"],"additionalProperties":false,"description":"One suggestion, in the search box’s own shape (kept from the old site, field names and all)."},"WikidataItem":{"type":"object","properties":{"qid":{"type":"string","description":"The item asked for."},"resolved_qid":{"description":"Present when Wikidata merged it: the item it now redirects to.","type":"string"},"label":{"type":["string","null"]},"description":{"type":["string","null"]},"image":{"anyOf":[{"type":"object","properties":{"filename":{"type":"string","description":"The item’s image on Wikimedia Commons."},"token":{"type":"string","description":"Proves the file came from this look-up, for adding it to the site."}},"required":["filename","token"],"additionalProperties":false},{"type":"null"}]},"handles":{"type":"array","items":{"type":"object","properties":{"platform_id":{"type":"string"},"value":{"type":"string"},"value_norm":{"type":"string","description":"Its key as the site stores it (letter case folded where the platform ignores it): the same handle as an entry’s with an equal `value_norm` (`GET /api/editor/{vhid}`)."}},"required":["platform_id","value","value_norm"],"additionalProperties":false},"description":"The handles the item states, as the Wikidata sync would store them."},"entries_on_item":{"type":"array","items":{"type":"object","properties":{"vhid":{"type":"string"},"short":{"type":["string","null"]},"title":{"type":"string"}},"required":["vhid","short","title"],"additionalProperties":false},"description":"The live entries already on the item."}},"required":["qid","label","description","image","handles","entries_on_item"],"additionalProperties":false,"description":"What one Wikidata item says, mapped the way the sync maps it."}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"vhk_<prefix>_<secret>","description":"An API key, made in the console, sent as `Authorization: Bearer vhk_<prefix>_<secret>`. It acts as its holder, never above the role it was made with; a read-only key may only GET. Never send a session cookie with it, and never put it in a query string."}}}}