diff --git a/openapi/developer-portal.json b/openapi/developer-portal.json index 738f095..bc10a79 100644 --- a/openapi/developer-portal.json +++ b/openapi/developer-portal.json @@ -132,6 +132,7 @@ "x-mint": { "href": "/api-reference/developer-portal/send-notification" }, + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -141,10 +142,13 @@ }, "example": { "app_id": "app_id", - "wallet_addresses": ["0x123", "0x456"], + "wallet_addresses": [ + "0x1234567890123456789012345678901234567890", + "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" + ], "title": "title", "message": "Hello ${username}, your transaction is complete!", - "mini_app_path": "mini_app_path" + "mini_app_path": "worldapp://mini-app?app_id=app_id" } } } @@ -159,6 +163,88 @@ } } } + }, + "400": { + "description": "Invalid request, inactive API key, or the app can't send notifications (`validation_error`, `api_key_inactive`, `external_app_not_allowed`, `unverified_app_limit_reached`, `not_allowed`).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "api_key_inactive": { + "value": { + "code": "api_key_inactive", + "detail": "API key is inactive.", + "attribute": "api_key" + } + }, + "unverified_app_limit_reached": { + "value": { + "code": "unverified_app_limit_reached", + "detail": "Unverified app limit reached", + "attribute": "notifications" + } + } + } + } + } + }, + "401": { + "description": "Missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "code": "unauthorized", + "detail": "API key is required.", + "attribute": "api_key" + } + } + } + }, + "403": { + "description": "API key rejected: `invalid_app` when the key's team doesn't own `app_id`, `invalid_api_key` when the secret doesn't match.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "invalid_app": { + "value": { + "code": "invalid_app", + "detail": "API key is not valid for this app.", + "attribute": "api_key" + } + }, + "invalid_api_key": { + "value": { + "code": "invalid_api_key", + "detail": "API key is not valid.", + "attribute": "api_key" + } + } + } + } + } + }, + "404": { + "description": "API key not found (`not_found`), or the app is banned or has no metadata (`app_not_found`, attribute `app`).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "code": "not_found", + "detail": "API key not found.", + "attribute": "api_key" + } + } + } } } } @@ -210,6 +296,21 @@ } } } + }, + "400": { + "description": "Invalid transaction ID", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "code": "invalid_parameter", + "detail": "Invalid transaction ID", + "attribute": "transaction_id" + } + } + } } } } @@ -313,6 +414,7 @@ "x-mint": { "href": "/api-reference/developer-portal/get-transaction-debug-url" }, + "security": [{ "bearerAuth": [] }], "parameters": [ { "name": "app_id", @@ -346,6 +448,88 @@ } } } + }, + "400": { + "description": "Invalid `app_id` (`validation_error`) or inactive API key (`api_key_inactive`).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "code": "api_key_inactive", + "detail": "API key is inactive.", + "attribute": "api_key" + } + } + } + }, + "401": { + "description": "Missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "code": "unauthorized", + "detail": "API key is required.", + "attribute": "api_key" + } + } + } + }, + "403": { + "description": "API key rejected: `invalid_app` when the key's team doesn't own `app_id`, `invalid_api_key` when the secret doesn't match.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "invalid_app": { + "value": { + "code": "invalid_app", + "detail": "API key is not valid for this app.", + "attribute": "api_key" + } + }, + "invalid_api_key": { + "value": { + "code": "invalid_api_key", + "detail": "API key is not valid.", + "attribute": "api_key" + } + } + } + } + } + }, + "404": { + "description": "API key not found (`not_found`), or no debug URL is available yet (`debug_url_not_available`).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "not_found": { + "value": { + "code": "not_found", + "detail": "API key not found.", + "attribute": "api_key" + } + }, + "debug_url_not_available": { + "value": { + "code": "debug_url_not_available", + "detail": "Debug URL is not yet available. Please try again later.", + "attribute": "debug" + } + } + } + } + } } } } @@ -392,6 +576,21 @@ } } } + }, + "426": { + "description": "Rejected by an app-version/client gate. Expects an approved client (e.g. World App); required header(s) aren't publicly documented.", + "content": { + "application/json": { + "example": { + "allowRetry": false, + "error": { + "success": false, + "code": "AU-001", + "message": "App update required" + } + } + } + } } } } @@ -403,6 +602,7 @@ "x-mint": { "href": "/api-reference/developer-portal/get-user-grant-cycle" }, + "security": [{ "bearerAuth": [] }], "parameters": [ { "name": "wallet_address", @@ -437,7 +637,86 @@ } }, "400": { - "description": "User not found / app not installed / no active cycles" + "description": "Invalid query (`validation_error`), inactive API key (`api_key_inactive`), or user not found / app not installed / no active cycles (`user_not_found_for_wallet_address`, `app_not_installed`, `no_active_grant_cycles`, attribute `user_grant_cycle`).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "validation_error": { + "value": { + "code": "validation_error", + "detail": "app_id is required", + "attribute": "app_id" + } + }, + "api_key_inactive": { + "value": { + "code": "api_key_inactive", + "detail": "API key is inactive.", + "attribute": "api_key" + } + } + } + } + } + }, + "401": { + "description": "Missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "code": "unauthorized", + "detail": "API key is required.", + "attribute": "api_key" + } + } + } + }, + "403": { + "description": "API key rejected: `invalid_app` when the key's team doesn't own `app_id`, `invalid_api_key` when the secret doesn't match.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "invalid_app": { + "value": { + "code": "invalid_app", + "detail": "API key is not valid for this app.", + "attribute": "api_key" + } + }, + "invalid_api_key": { + "value": { + "code": "invalid_api_key", + "detail": "API key is not valid.", + "attribute": "api_key" + } + } + } + } + } + }, + "404": { + "description": "API key not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "code": "not_found", + "detail": "API key not found.", + "attribute": "api_key" + } + } + } } } } @@ -449,6 +728,7 @@ "x-mint": { "href": "/api-reference/developer-portal/create-incognito-action" }, + "security": [{ "bearerAuth": [] }], "parameters": [ { "name": "app_id", @@ -479,6 +759,88 @@ } } } + }, + "400": { + "description": "Invalid request (`validation_error`), inactive API key (`api_key_inactive`), or the action already exists (`constraint-violation`).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "api_key_inactive": { + "value": { + "code": "api_key_inactive", + "detail": "API key is inactive.", + "attribute": "api_key" + } + }, + "constraint-violation": { + "value": { + "code": "constraint-violation", + "detail": "Action already exists.", + "attribute": "action" + } + } + } + } + } + }, + "401": { + "description": "Missing API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "code": "unauthorized", + "detail": "API key is required.", + "attribute": "api_key" + } + } + } + }, + "403": { + "description": "API key rejected: `invalid_app` when the key's team doesn't own `app_id`, `invalid_api_key` when the secret doesn't match.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "invalid_app": { + "value": { + "code": "invalid_app", + "detail": "API key is not valid for this app.", + "attribute": "api_key" + } + }, + "invalid_api_key": { + "value": { + "code": "invalid_api_key", + "detail": "API key is not valid.", + "attribute": "api_key" + } + } + } + } + } + }, + "404": { + "description": "API key not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "code": "not_found", + "detail": "API key not found.", + "attribute": "api_key" + } + } + } } } } @@ -532,7 +894,7 @@ }, "responses": { "200": { - "description": "OK", + "description": "OK. The `app_id` in the example is illustrative; a nonexistent or inactive app_id returns 404.", "content": { "application/json": { "schema": { @@ -571,11 +933,26 @@ }, "example": { "code": "required", - "detail": "This attribute is required.", + "detail": "No action found for this app.", "attribute": "action" } } } + }, + "404": { + "description": "App not found or inactive", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "code": "not_found", + "detail": "We couldn't find an app with this ID. Action may be inactive.", + "attribute": null + } + } + } } } } @@ -583,7 +960,7 @@ "/api/v1/jwks": { "get": { "summary": "Get JWK Keys", - "description": "Retrieve JWKs (public keys) used to verify JWT signatures for verification requests.", + "description": "Retrieve JWKs (public keys) used to verify JWT signatures for verification requests. Responses aren't cached at the edge, so cache keys client-side: refetch when a token's `kid` is unknown, and expire the cache after a bounded TTL (e.g. 1 hour) so removed keys stop being trusted.", "x-mint": { "href": "/api-reference/developer-portal/get-jwk-keys" }, @@ -626,7 +1003,7 @@ "/api/v1/graphql": { "post": { "summary": "GraphQL Proxy", - "description": "Proxy endpoint for Developer Portal GraphQL queries and mutations. Supports user JWTs and API keys.", + "description": "Proxy endpoint for Developer Portal GraphQL queries and mutations. Supports user JWTs and API keys. The example query requires authentication; an unauthenticated request returns HTTP 200 with a GraphQL `validation-failed` error (`field 'app' not found in type: 'query_root'`), not a 401.", "x-mint": { "href": "/api-reference/developer-portal/graphql-proxy" }, @@ -656,22 +1033,54 @@ } } }, - "401": { - "description": "Unauthenticated request", + "400": { + "description": "Body isn't JSON: wrong `Content-Type` (`invalid_content_type`) or malformed JSON (`invalid_json`).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "invalid_content_type": { + "value": { + "code": "invalid_content_type", + "detail": "Content-Type must be application/json.", + "attribute": "content-type" + } + }, + "invalid_json": { + "value": { + "code": "invalid_json", + "detail": "Request body must be valid JSON.", + "attribute": null + } + } } } } }, - "415": { - "description": "Invalid JSON payload", + "401": { + "description": "Rejected `api_` API key: not found, inactive, or wrong secret.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "invalid_or_inactive": { + "value": { + "code": "unauthenticated", + "detail": "Invalid or inactive API key.", + "attribute": null + } + }, + "invalid_secret": { + "value": { + "code": "unauthenticated", + "detail": "Invalid API key secret.", + "attribute": null + } + } } } } @@ -885,6 +1294,7 @@ "secure_document", "document", "device", + "face", "selfie" ], "description": "The legacy verification level. Use `selfie` for Selfie Check. The historical `face` value remains accepted for backward compatibility and behaves the same as `selfie`." @@ -934,12 +1344,14 @@ "maxLength": 200 }, "mini_app_path": { - "type": "string" + "type": "string", + "description": "Deeplink to open when the user taps the notification: a WorldApp/World ID mini app deeplink (`worldapp://mini-app?app_id=...` or `worldid://mini-app?app_id=...`, where `app_id` must match the request's `app_id`), a Deep Face Universal Link (`https://world.org/verify?t=deepface`), or a Deep Face deeplink (`worldapp://verify?t=deepface`, `worldid://verify?t=deepface`, `worldidstg://verify?t=deepface`)." }, "app_id": { "type": "string" } - } + }, + "not": { "required": ["localisations"] } }, { "type": "object", @@ -966,7 +1378,8 @@ "description": "Localized notification content. Include at least `en`." }, "mini_app_path": { - "type": "string" + "type": "string", + "description": "Deeplink to open when the user taps the notification: a WorldApp/World ID mini app deeplink (`worldapp://mini-app?app_id=...` or `worldid://mini-app?app_id=...`, where `app_id` must match the request's `app_id`), a Deep Face Universal Link (`https://world.org/verify?t=deepface`), or a Deep Face deeplink (`worldapp://verify?t=deepface`, `worldid://verify?t=deepface`, `worldidstg://verify?t=deepface`)." }, "app_id": { "type": "string" @@ -974,7 +1387,7 @@ } } ], - "description": "Supports legacy payload (`title` + `message`) and localized payload (`localisations`). If both are provided, localized payload is used." + "description": "Supports legacy payload (`title` + `message`) and localized payload (`localisations`). If `localisations` is present, the localized payload is used and `title`/`message` are ignored." }, "SendNotificationResultItem": { "type": "object", @@ -1211,32 +1624,6 @@ } } }, - "CreditBorrower": { - "type": "object", - "required": ["state", "score"], - "properties": { - "state": { - "type": "string", - "enum": ["INACTIVE", "ACTIVE", "DEFAULTED"], - "description": "Borrower status on Credit" - }, - "score": { - "type": "integer", - "format": "int32", - "minimum": 0, - "description": "The borrower's credit score" - } - } - }, - "CreditError": { - "type": "object", - "properties": { - "error": { - "type": "string", - "description": "Error message" - } - } - }, "ErrorResponse": { "type": "object", "properties": { @@ -1249,6 +1636,14 @@ "attribute": { "type": "string", "nullable": true + }, + "app_id": { + "type": "string", + "description": "Included when the request identifies an app." + }, + "team_id": { + "type": "string", + "description": "Included when the route has resolved the app's team." } } }, @@ -1353,7 +1748,7 @@ }, "environment": { "type": "string", - "enum": ["production", "staging"], + "enum": ["production", "staging", "sandbox"], "default": "production" }, "integrity_bundle": { @@ -1618,7 +2013,7 @@ }, "environment": { "type": "string", - "enum": ["production", "staging"] + "enum": ["production", "staging", "sandbox"] }, "session_id": { "type": "string",