{"openapi":"3.1.0","info":{"title":"WME Sync API","version":"1.0.0","description":"Sync settings and data for Waze Map Editor userscripts across browsers.\n\n1. The user links their Waze username and sets a PIN on the WME Sync dashboard.\n2. Your script calls `POST /api/token` with the username and PIN, and stores the returned tokens.\n3. Send `Authorization: Bearer <access_token>` to the `/api/data` endpoints. On a 401, call `POST /api/token/refresh`.\n\nData is namespaced per Waze username and per script_id. Limits: 256 KB per value, 5 MB per Waze username."},"servers":[{"url":"https://sync.wazetools.com"}],"tags":[{"name":"Tokens","description":"Exchange username + PIN for tokens, refresh and revoke them."},{"name":"Data","description":"Per-script JSON key/value storage."},{"name":"Meta","description":"Identity and service info."}],"paths":{"/api/token":{"post":{"tags":["Tokens"],"summary":"Exchange Waze username + PIN for tokens","description":"The access token is valid for 180 days, the refresh token for 730 days. Store both (e.g. with GM_setValue). At most 5 failed PINs per username and 20 per IP are allowed per 15 minutes.","operationId":"postApiToken","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}}},"responses":{"200":{"description":"Token pair.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"400":{"description":"Invalid script_id, key or request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid username or PIN.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many failed attempts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/token/refresh":{"post":{"tags":["Tokens"],"summary":"Rotate a token pair","description":"Returns a fresh access + refresh token and invalidates the old pair. Each refresh token works once: presenting an already-used refresh token revokes the whole token family.","operationId":"postApiTokenRefresh","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}}},"responses":{"200":{"description":"New token pair.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"description":"Invalid, expired, revoked or reused refresh token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/token/revoke":{"post":{"tags":["Tokens"],"summary":"Revoke the current token","description":"Revokes the calling access token and its refresh token (e.g. on sign-out).","operationId":"postApiTokenRevoke","security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Missing, invalid, expired or revoked access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/me":{"get":{"tags":["Meta"],"summary":"Current identity and storage usage","operationId":"getApiMe","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Identity info.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"Missing, invalid, expired or revoked access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/data/{script}":{"get":{"tags":["Data"],"summary":"Get all keys for a script","operationId":"getApiDataScript","parameters":[{"name":"script","in":"path","required":true,"description":"Script namespace (script_id).","schema":{"type":"string","pattern":"^[a-zA-Z0-9._-]{1,64}$","description":"Your script’s namespace, e.g. \"wme-my-script\".","example":"wme-my-script"}},{"name":"keys_only","description":"Set to 1 to omit values (just versions, sizes and timestamps).","schema":{"type":"string","enum":["1","true"]},"in":"query","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"All entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScriptEntries"}}}},"400":{"description":"Invalid script_id, key or request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Data"],"summary":"Bulk upsert keys","description":"Writes up to 100 keys at once (unconditionally — no version checks).","operationId":"patchApiDataScript","parameters":[{"name":"script","in":"path","required":true,"description":"Script namespace (script_id).","schema":{"type":"string","pattern":"^[a-zA-Z0-9._-]{1,64}$","description":"Your script’s namespace, e.g. \"wme-my-script\".","example":"wme-my-script"}}],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUpsertRequest"}}}},"responses":{"200":{"description":"New version per key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkWriteResult"}}}},"400":{"description":"Invalid script_id, key or request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Value over 256 KB, or the identity's 5 MB quota would be exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Data"],"summary":"Delete every key for a script","operationId":"deleteApiDataScript","parameters":[{"name":"script","in":"path","required":true,"description":"Script namespace (script_id).","schema":{"type":"string","pattern":"^[a-zA-Z0-9._-]{1,64}$","description":"Your script’s namespace, e.g. \"wme-my-script\".","example":"wme-my-script"}}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Number of keys deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["deleted"]}}}},"400":{"description":"Invalid script_id, key or request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/data/{script}/{key}":{"get":{"tags":["Data"],"summary":"Get one key","operationId":"getApiDataScriptKey","parameters":[{"name":"script","in":"path","required":true,"description":"Script namespace (script_id).","schema":{"type":"string","pattern":"^[a-zA-Z0-9._-]{1,64}$","description":"Your script’s namespace, e.g. \"wme-my-script\".","example":"wme-my-script"}},{"name":"key","in":"path","required":true,"description":"Key within the script.","schema":{"type":"string","pattern":"^[a-zA-Z0-9._-]{1,128}$","example":"settings"}}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The entry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Entry"}}}},"400":{"description":"Invalid script_id, key or request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Key not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"tags":["Data"],"summary":"Set one key","description":"Stores any JSON value (max 256 KB). Pass `if_version` for optimistic concurrency.","operationId":"putApiDataScriptKey","parameters":[{"name":"script","in":"path","required":true,"description":"Script namespace (script_id).","schema":{"type":"string","pattern":"^[a-zA-Z0-9._-]{1,64}$","description":"Your script’s namespace, e.g. \"wme-my-script\".","example":"wme-my-script"}},{"name":"key","in":"path","required":true,"description":"Key within the script.","schema":{"type":"string","pattern":"^[a-zA-Z0-9._-]{1,128}$","example":"settings"}}],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutValueRequest"}}}},"responses":{"200":{"description":"Written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WriteResult"}}}},"400":{"description":"Invalid script_id, key or request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`if_version` did not match the stored version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Value over 256 KB, or the identity's 5 MB quota would be exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Data"],"summary":"Delete one key","operationId":"deleteApiDataScriptKey","parameters":[{"name":"script","in":"path","required":true,"description":"Script namespace (script_id).","schema":{"type":"string","pattern":"^[a-zA-Z0-9._-]{1,64}$","description":"Your script’s namespace, e.g. \"wme-my-script\".","example":"wme-my-script"}},{"name":"key","in":"path","required":true,"description":"Key within the script.","schema":{"type":"string","pattern":"^[a-zA-Z0-9._-]{1,128}$","example":"settings"}}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Deleted."},"400":{"description":"Invalid script_id, key or request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid, expired or revoked access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Key not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/health":{"get":{"tags":["Meta"],"summary":"Health check","operationId":"getApiHealth","security":[],"responses":{"200":{"description":"OK.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"ok"},"ts":{"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":["status","ts"]}}}}}}}},"components":{"schemas":{"TokenRequest":{"type":"object","properties":{"username":{"type":"string","minLength":1,"maxLength":64,"pattern":"^\\S+$","description":"Waze username (case-insensitive).","example":"my_waze_name"},"pin":{"type":"string","pattern":"^\\d{6,12}$","description":"6–12 digit PIN set for this username on the dashboard.","example":"123456"},"label":{"description":"Free-form label shown on the dashboard, e.g. your script name + browser.","type":"string","maxLength":200}},"required":["username","pin"]},"RefreshRequest":{"type":"object","properties":{"refresh_token":{"type":"string","minLength":1}},"required":["refresh_token"]},"TokenResponse":{"type":"object","properties":{"access_token":{"type":"string","example":"wms_at_…"},"refresh_token":{"type":"string","example":"wms_rt_…"},"token_type":{"type":"string","const":"Bearer"},"access_expires_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))$"},"refresh_expires_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))$"},"username":{"type":"string"}},"required":["access_token","refresh_token","token_type","access_expires_at","refresh_expires_at","username"]},"PutValueRequest":{"type":"object","properties":{"value":{"description":"Any JSON value (object, array, string, number, boolean or null)."},"if_version":{"description":"Optimistic concurrency: only write if the stored version equals this. Use 0 to require that the key does not exist yet. Mismatch → 409.","type":"integer","minimum":0,"maximum":9007199254740991}},"required":["value"]},"BulkUpsertRequest":{"type":"object","properties":{"entries":{"type":"object","propertyNames":{"type":"string","pattern":"^[a-zA-Z0-9._-]{1,128}$","example":"settings"},"additionalProperties":{"description":"Any JSON value (object, array, string, number, boolean or null)."},"description":"Map of key → JSON value (at most 100)."}},"required":["entries"]},"WriteResult":{"type":"object","properties":{"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"updated_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":["version","updated_at"]},"BulkWriteResult":{"type":"object","properties":{"entries":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/WriteResult"}}},"required":["entries"]},"Entry":{"type":"object","properties":{"value":{},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"size":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Stored size in bytes."},"updated_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":["value","version","size","updated_at"]},"ScriptEntries":{"type":"object","properties":{"script_id":{"type":"string"},"entries":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"$ref":"#/components/schemas/Entry"},"description":"Map of key → entry. With ?keys_only=1, entries have no `value`."}},"required":["script_id","entries"]},"ScriptSummary":{"type":"object","properties":{"script_id":{"type":"string"},"keys":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"updated_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":["script_id","keys","bytes","updated_at"]},"Me":{"type":"object","properties":{"username":{"type":"string"},"bytes_used":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"bytes_limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"scripts":{"type":"array","items":{"$ref":"#/components/schemas/ScriptSummary"}}},"required":["username","bytes_used","bytes_limit","scripts"]},"Error":{"type":"object","properties":{"error":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["error"],"description":"On 409, `version` is the currently stored version (0 if the key is absent)."}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Access token (`wms_at_…`) from POST /api/token or /api/token/refresh."}}}}