From ee394223b17928d63de5e266f3fd77977717b624 Mon Sep 17 00:00:00 2001 From: Dhruv Pareek Date: Thu, 7 May 2026 17:08:56 -0700 Subject: [PATCH 1/2] Add internal account email update endpoint --- mintlify/openapi.yaml | 127 ++++++++++++++++++ openapi.yaml | 127 ++++++++++++++++++ .../components/schemas/errors/Error409.yaml | 2 + .../InternalAccountEmailUpdateRequest.yaml | 16 +++ .../InternalAccountEmailUpdateResponse.yaml | 21 +++ openapi/openapi.yaml | 2 + .../internal_accounts_{id}_email.yaml | 123 +++++++++++++++++ 7 files changed, 418 insertions(+) create mode 100644 openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateRequest.yaml create mode 100644 openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateResponse.yaml create mode 100644 openapi/paths/internal_accounts/internal_accounts_{id}_email.yaml diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index ceb46df9..75b177fc 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -3734,6 +3734,97 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' + /internal-accounts/{id}/email: + patch: + summary: Update internal account email + description: | + Update the email address used for the `EMAIL_OTP` authentication credential on an Embedded Wallet internal account. + + This is a two-step signed-retry flow: + + 1. Call `PATCH /internal-accounts/{id}/email` with the request body `{ "email": "new.email@example.com" }` and no signature headers. Grid returns `202` with `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Use the session API keypair of a verified authentication credential on the same internal account to build an API-key stamp over `payloadToSign`, then retry with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The retry body must carry the same `email` submitted in step 1. The signed retry returns `200` with the updated email address. + operationId: updateInternalAccountEmail + tags: + - Internal Accounts + security: + - BasicAuth: [] + parameters: + - name: id + in: path + description: The id of the internal account whose email address should be updated. + required: true + schema: + type: string + - name: Grid-Wallet-Signature + in: header + required: false + description: Full API-key stamp built over the prior `payloadToSign` with the session API keypair of a verified authentication credential on the target internal account. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - name: Request-Id + in: header + required: false + description: The `requestId` returned in a prior `202` response, echoed back on the signed retry so the server can correlate it with the issued challenge. Required on the signed retry; must be paired with `Grid-Wallet-Signature`. + schema: + type: string + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/InternalAccountEmailUpdateRequest' + examples: + updateEmail: + summary: Update email request (both steps) + value: + email: new.email@example.com + responses: + '200': + description: Signed retry accepted. Returns the updated email address. + content: + application/json: + schema: + $ref: '#/components/schemas/InternalAccountEmailUpdateResponse' + '202': + description: Challenge issued. The response contains `payloadToSign` plus a `requestId`. Build an API-key stamp over `payloadToSign` with the session API keypair and echo `requestId` on the retry. + content: + application/json: + schema: + $ref: '#/components/schemas/SignedRequestChallenge' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending email update challenge for this internal account, when the `Request-Id` does not match an unexpired pending challenge, or when the retry's `email` does not match the one bound into `payloadToSign` on the initial call. + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Internal account not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned when the supplied email address is already associated with an `EMAIL_OTP` credential on this or another internal account. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' /auth/credentials: post: summary: Create an authentication credential @@ -7869,9 +7960,11 @@ components: |------------|-------------| | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval | | UMA_ADDRESS_EXISTS | UMA address already exists | + | EMAIL_OTP_EMAIL_ALREADY_EXISTS | Email address is already associated with an EMAIL_OTP credential | enum: - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL - UMA_ADDRESS_EXISTS + - EMAIL_OTP_EMAIL_ALREADY_EXISTS message: type: string description: Error message @@ -15415,6 +15508,40 @@ components: format: date-time description: Timestamp after which this challenge is no longer valid. The signed retry must be submitted before this time. example: '2026-04-08T15:35:00Z' + InternalAccountEmailUpdateRequest: + title: Internal Account Email Update Request + description: Request body for `PATCH /internal-accounts/{id}/email`. The `email` is required on both steps of the signed-retry flow. On step 1 Grid binds it into `payloadToSign`; on step 2 the client echoes the same `email` back and Grid updates the email address used for the internal account's `EMAIL_OTP` credential. + type: object + required: + - email + properties: + email: + type: string + format: email + description: New email address to associate with the internal account's `EMAIL_OTP` credential. + example: new.email@example.com + InternalAccountEmailUpdateResponse: + title: Internal Account Email Update Response + type: object + required: + - id + - email + - updatedAt + properties: + id: + type: string + description: The id of the internal account whose email address was updated. + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + email: + type: string + format: email + description: Updated email address associated with the internal account's `EMAIL_OTP` credential. + example: new.email@example.com + updatedAt: + type: string + format: date-time + description: Timestamp when the email address was updated. + example: '2026-04-08T15:35:02Z' AuthMethodType: type: string enum: diff --git a/openapi.yaml b/openapi.yaml index ceb46df9..75b177fc 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -3734,6 +3734,97 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' + /internal-accounts/{id}/email: + patch: + summary: Update internal account email + description: | + Update the email address used for the `EMAIL_OTP` authentication credential on an Embedded Wallet internal account. + + This is a two-step signed-retry flow: + + 1. Call `PATCH /internal-accounts/{id}/email` with the request body `{ "email": "new.email@example.com" }` and no signature headers. Grid returns `202` with `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Use the session API keypair of a verified authentication credential on the same internal account to build an API-key stamp over `payloadToSign`, then retry with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The retry body must carry the same `email` submitted in step 1. The signed retry returns `200` with the updated email address. + operationId: updateInternalAccountEmail + tags: + - Internal Accounts + security: + - BasicAuth: [] + parameters: + - name: id + in: path + description: The id of the internal account whose email address should be updated. + required: true + schema: + type: string + - name: Grid-Wallet-Signature + in: header + required: false + description: Full API-key stamp built over the prior `payloadToSign` with the session API keypair of a verified authentication credential on the target internal account. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - name: Request-Id + in: header + required: false + description: The `requestId` returned in a prior `202` response, echoed back on the signed retry so the server can correlate it with the issued challenge. Required on the signed retry; must be paired with `Grid-Wallet-Signature`. + schema: + type: string + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/InternalAccountEmailUpdateRequest' + examples: + updateEmail: + summary: Update email request (both steps) + value: + email: new.email@example.com + responses: + '200': + description: Signed retry accepted. Returns the updated email address. + content: + application/json: + schema: + $ref: '#/components/schemas/InternalAccountEmailUpdateResponse' + '202': + description: Challenge issued. The response contains `payloadToSign` plus a `requestId`. Build an API-key stamp over `payloadToSign` with the session API keypair and echo `requestId` on the retry. + content: + application/json: + schema: + $ref: '#/components/schemas/SignedRequestChallenge' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending email update challenge for this internal account, when the `Request-Id` does not match an unexpired pending challenge, or when the retry's `email` does not match the one bound into `payloadToSign` on the initial call. + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Internal account not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned when the supplied email address is already associated with an `EMAIL_OTP` credential on this or another internal account. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' /auth/credentials: post: summary: Create an authentication credential @@ -7869,9 +7960,11 @@ components: |------------|-------------| | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval | | UMA_ADDRESS_EXISTS | UMA address already exists | + | EMAIL_OTP_EMAIL_ALREADY_EXISTS | Email address is already associated with an EMAIL_OTP credential | enum: - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL - UMA_ADDRESS_EXISTS + - EMAIL_OTP_EMAIL_ALREADY_EXISTS message: type: string description: Error message @@ -15415,6 +15508,40 @@ components: format: date-time description: Timestamp after which this challenge is no longer valid. The signed retry must be submitted before this time. example: '2026-04-08T15:35:00Z' + InternalAccountEmailUpdateRequest: + title: Internal Account Email Update Request + description: Request body for `PATCH /internal-accounts/{id}/email`. The `email` is required on both steps of the signed-retry flow. On step 1 Grid binds it into `payloadToSign`; on step 2 the client echoes the same `email` back and Grid updates the email address used for the internal account's `EMAIL_OTP` credential. + type: object + required: + - email + properties: + email: + type: string + format: email + description: New email address to associate with the internal account's `EMAIL_OTP` credential. + example: new.email@example.com + InternalAccountEmailUpdateResponse: + title: Internal Account Email Update Response + type: object + required: + - id + - email + - updatedAt + properties: + id: + type: string + description: The id of the internal account whose email address was updated. + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + email: + type: string + format: email + description: Updated email address associated with the internal account's `EMAIL_OTP` credential. + example: new.email@example.com + updatedAt: + type: string + format: date-time + description: Timestamp when the email address was updated. + example: '2026-04-08T15:35:02Z' AuthMethodType: type: string enum: diff --git a/openapi/components/schemas/errors/Error409.yaml b/openapi/components/schemas/errors/Error409.yaml index b033a67f..86617563 100644 --- a/openapi/components/schemas/errors/Error409.yaml +++ b/openapi/components/schemas/errors/Error409.yaml @@ -16,9 +16,11 @@ properties: |------------|-------------| | TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval | | UMA_ADDRESS_EXISTS | UMA address already exists | + | EMAIL_OTP_EMAIL_ALREADY_EXISTS | Email address is already associated with an EMAIL_OTP credential | enum: - TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL - UMA_ADDRESS_EXISTS + - EMAIL_OTP_EMAIL_ALREADY_EXISTS message: type: string description: Error message diff --git a/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateRequest.yaml b/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateRequest.yaml new file mode 100644 index 00000000..3ea8aa7e --- /dev/null +++ b/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateRequest.yaml @@ -0,0 +1,16 @@ +title: Internal Account Email Update Request +description: >- + Request body for `PATCH /internal-accounts/{id}/email`. The `email` + is required on both steps of the signed-retry flow. On step 1 Grid + binds it into `payloadToSign`; on step 2 the client echoes the same + `email` back and Grid updates the email address used for the + internal account's `EMAIL_OTP` credential. +type: object +required: + - email +properties: + email: + type: string + format: email + description: New email address to associate with the internal account's `EMAIL_OTP` credential. + example: new.email@example.com diff --git a/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateResponse.yaml b/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateResponse.yaml new file mode 100644 index 00000000..069ad0cc --- /dev/null +++ b/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateResponse.yaml @@ -0,0 +1,21 @@ +title: Internal Account Email Update Response +type: object +required: + - id + - email + - updatedAt +properties: + id: + type: string + description: The id of the internal account whose email address was updated. + example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + email: + type: string + format: email + description: Updated email address associated with the internal account's `EMAIL_OTP` credential. + example: new.email@example.com + updatedAt: + type: string + format: date-time + description: Timestamp when the email address was updated. + example: '2026-04-08T15:35:02Z' diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 771b4131..d2ae90bf 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -201,6 +201,8 @@ paths: $ref: paths/tokens/tokens_{tokenId}.yaml /internal-accounts/{id}/export: $ref: paths/internal_accounts/internal_accounts_{id}_export.yaml + /internal-accounts/{id}/email: + $ref: paths/internal_accounts/internal_accounts_{id}_email.yaml /auth/credentials: $ref: paths/auth/auth_credentials.yaml /auth/credentials/{id}: diff --git a/openapi/paths/internal_accounts/internal_accounts_{id}_email.yaml b/openapi/paths/internal_accounts/internal_accounts_{id}_email.yaml new file mode 100644 index 00000000..709e0eb9 --- /dev/null +++ b/openapi/paths/internal_accounts/internal_accounts_{id}_email.yaml @@ -0,0 +1,123 @@ +patch: + summary: Update internal account email + description: > + Update the email address used for the `EMAIL_OTP` authentication + credential on an Embedded Wallet internal account. + + + This is a two-step signed-retry flow: + + + 1. Call `PATCH /internal-accounts/{id}/email` with the request body + `{ "email": "new.email@example.com" }` and no signature headers. + Grid returns `202` with `payloadToSign`, `requestId`, and + `expiresAt`. + + + 2. Use the session API keypair of a verified authentication credential + on the same internal account to build an API-key stamp over + `payloadToSign`, then retry with that full stamp as the + `Grid-Wallet-Signature` header and the `requestId` echoed back as + the `Request-Id` header. The retry body must carry the same `email` + submitted in step 1. The signed retry returns `200` with the updated + email address. + operationId: updateInternalAccountEmail + tags: + - Internal Accounts + security: + - BasicAuth: [] + parameters: + - name: id + in: path + description: The id of the internal account whose email address should be updated. + required: true + schema: + type: string + - name: Grid-Wallet-Signature + in: header + required: false + description: >- + Full API-key stamp built over the prior `payloadToSign` with + the session API keypair of a verified authentication credential + on the target internal account. Required on the signed retry; + ignored on the initial call. + schema: + type: string + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - name: Request-Id + in: header + required: false + description: >- + The `requestId` returned in a prior `202` response, echoed back + on the signed retry so the server can correlate it with the + issued challenge. Required on the signed retry; must be paired + with `Grid-Wallet-Signature`. + schema: + type: string + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../components/schemas/internal_accounts/InternalAccountEmailUpdateRequest.yaml + examples: + updateEmail: + summary: Update email request (both steps) + value: + email: new.email@example.com + responses: + '200': + description: Signed retry accepted. Returns the updated email address. + content: + application/json: + schema: + $ref: ../../components/schemas/internal_accounts/InternalAccountEmailUpdateResponse.yaml + '202': + description: >- + Challenge issued. The response contains `payloadToSign` plus a + `requestId`. Build an API-key stamp over `payloadToSign` with + the session API keypair and echo `requestId` on the retry. + content: + application/json: + schema: + $ref: ../../components/schemas/common/SignedRequestChallenge.yaml + '400': + description: Bad request + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error400.yaml + '401': + description: >- + Unauthorized. Returned when the provided `Grid-Wallet-Signature` + is missing, malformed, or does not match a pending email update + challenge for this internal account, when the `Request-Id` does + not match an unexpired pending challenge, or when the retry's + `email` does not match the one bound into `payloadToSign` on + the initial call. + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error401.yaml + '409': + description: >- + Conflict. Returned when the supplied email address is already + associated with an `EMAIL_OTP` credential on this or another + internal account. + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error409.yaml + '404': + description: Internal account not found + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error404.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error500.yaml From 148b0e03bfd3396f5278d90e4410027cefb80001 Mon Sep 17 00:00:00 2001 From: Dhruv Pareek Date: Mon, 11 May 2026 15:55:26 -0700 Subject: [PATCH 2/2] Move email OTP update to auth credential patch --- .stainless/stainless.yml | 4 + mintlify/openapi.yaml | 247 +++++++++--------- openapi.yaml | 247 +++++++++--------- .../auth/AuthCredentialUpdateRequest.yaml | 15 ++ .../auth/AuthSignedRequestChallenge.yaml | 22 +- .../InternalAccountEmailUpdateRequest.yaml | 16 -- .../InternalAccountEmailUpdateResponse.yaml | 21 -- openapi/openapi.yaml | 2 - openapi/paths/auth/auth_credentials_{id}.yaml | 147 +++++++++++ .../internal_accounts_{id}_email.yaml | 123 --------- 10 files changed, 418 insertions(+), 426 deletions(-) create mode 100644 openapi/components/schemas/auth/AuthCredentialUpdateRequest.yaml delete mode 100644 openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateRequest.yaml delete mode 100644 openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateResponse.yaml delete mode 100644 openapi/paths/internal_accounts/internal_accounts_{id}_email.yaml diff --git a/.stainless/stainless.yml b/.stainless/stainless.yml index feafbfed..7345cc18 100644 --- a/.stainless/stainless.yml +++ b/.stainless/stainless.yml @@ -437,6 +437,9 @@ resources: verify: endpoint: post /auth/credentials/{id}/verify body_param_name: AuthCredentialVerifyRequest + update: + endpoint: patch /auth/credentials/{id} + body_param_name: AuthCredentialUpdateRequest challenge: post /auth/credentials/{id}/challenge list: endpoint: get /auth/credentials @@ -448,6 +451,7 @@ resources: auth_session: '#/components/schemas/AuthSession' auth_credential_list_response: '#/components/schemas/AuthCredentialListResponse' auth_credential_create_request: '#/components/schemas/AuthCredentialCreateRequest' + auth_credential_update_request: '#/components/schemas/AuthCredentialUpdateRequest' auth_credential_verify_request: '#/components/schemas/AuthCredentialVerifyRequest' auth_credential_create_request_one_of: '#/components/schemas/AuthCredentialCreateRequestOneOf' auth_credential_verify_request_one_of: '#/components/schemas/AuthCredentialVerifyRequestOneOf' diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 75b177fc..bf6fdd7b 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -3734,97 +3734,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /internal-accounts/{id}/email: - patch: - summary: Update internal account email - description: | - Update the email address used for the `EMAIL_OTP` authentication credential on an Embedded Wallet internal account. - - This is a two-step signed-retry flow: - - 1. Call `PATCH /internal-accounts/{id}/email` with the request body `{ "email": "new.email@example.com" }` and no signature headers. Grid returns `202` with `payloadToSign`, `requestId`, and `expiresAt`. - - 2. Use the session API keypair of a verified authentication credential on the same internal account to build an API-key stamp over `payloadToSign`, then retry with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The retry body must carry the same `email` submitted in step 1. The signed retry returns `200` with the updated email address. - operationId: updateInternalAccountEmail - tags: - - Internal Accounts - security: - - BasicAuth: [] - parameters: - - name: id - in: path - description: The id of the internal account whose email address should be updated. - required: true - schema: - type: string - - name: Grid-Wallet-Signature - in: header - required: false - description: Full API-key stamp built over the prior `payloadToSign` with the session API keypair of a verified authentication credential on the target internal account. Required on the signed retry; ignored on the initial call. - schema: - type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - - name: Request-Id - in: header - required: false - description: The `requestId` returned in a prior `202` response, echoed back on the signed retry so the server can correlate it with the issued challenge. Required on the signed retry; must be paired with `Grid-Wallet-Signature`. - schema: - type: string - example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/InternalAccountEmailUpdateRequest' - examples: - updateEmail: - summary: Update email request (both steps) - value: - email: new.email@example.com - responses: - '200': - description: Signed retry accepted. Returns the updated email address. - content: - application/json: - schema: - $ref: '#/components/schemas/InternalAccountEmailUpdateResponse' - '202': - description: Challenge issued. The response contains `payloadToSign` plus a `requestId`. Build an API-key stamp over `payloadToSign` with the session API keypair and echo `requestId` on the retry. - content: - application/json: - schema: - $ref: '#/components/schemas/SignedRequestChallenge' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending email update challenge for this internal account, when the `Request-Id` does not match an unexpired pending challenge, or when the retry's `email` does not match the one bound into `payloadToSign` on the initial call. - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Internal account not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict. Returned when the supplied email address is already associated with an `EMAIL_OTP` credential on this or another internal account. - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' /auth/credentials: post: summary: Create an authentication credential @@ -4046,6 +3955,114 @@ paths: schema: $ref: '#/components/schemas/Error500' /auth/credentials/{id}: + patch: + summary: Update an authentication credential + description: | + Update mutable fields on an authentication credential for an Embedded Wallet internal account. Today this supports updating the email address used by an `EMAIL_OTP` credential. + + This is a two-step signed-retry flow: + + 1. Call `PATCH /auth/credentials/{id}` with the request body `{ "email": "new.email@example.com" }` and no signature headers. Grid returns `202` with `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Use the session API keypair of a verified authentication credential on the same internal account to build an API-key stamp over `payloadToSign`, then retry with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The retry body must carry the same update fields submitted in step 1. The signed retry returns `200` with the updated `AuthMethod`. + operationId: updateAuthCredential + tags: + - Embedded Wallet Auth + security: + - BasicAuth: [] + parameters: + - name: id + in: path + description: The id of the authentication credential to update (the `id` field of the `AuthMethod` returned from `POST /auth/credentials`). + required: true + schema: + type: string + - name: Grid-Wallet-Signature + in: header + required: false + description: Full API-key stamp built over the prior `payloadToSign` with the session API keypair of a verified authentication credential on the same internal account. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - name: Request-Id + in: header + required: false + description: The `requestId` returned in a prior `202` response, echoed back on the signed retry so the server can correlate it with the issued challenge. Required on the signed retry; must be paired with `Grid-Wallet-Signature`. + schema: + type: string + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AuthCredentialUpdateRequest' + examples: + updateEmail: + summary: Update email OTP address request (both steps) + value: + email: new.email@example.com + responses: + '200': + description: Signed retry accepted. Returns the updated authentication credential. + content: + application/json: + schema: + $ref: '#/components/schemas/AuthMethodResponse' + examples: + emailOtp: + summary: Email OTP credential updated + value: + id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001 + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + type: EMAIL_OTP + nickname: new.email@example.com + createdAt: '2026-04-08T15:30:01Z' + updatedAt: '2026-04-08T15:35:02Z' + '202': + description: Challenge issued. The response contains `payloadToSign` plus a `requestId`. Build an API-key stamp over `payloadToSign` with the session API keypair and echo `requestId` on the retry. + content: + application/json: + schema: + $ref: '#/components/schemas/AuthSignedRequestChallenge' + examples: + emailOtp: + summary: Email OTP credential update challenge + value: + type: EMAIL_OTP + payloadToSign: '{"requestId":"7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","id":"AuthMethod:019542f5-b3e7-1d02-0000-000000000001","email":"new.email@example.com","expiresAt":"2026-04-08T15:35:00Z"}' + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + expiresAt: '2026-04-08T15:35:00Z' + '400': + description: Bad request. Also returned when attempting to update a field that is not supported for the target credential type. + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending credential update challenge, when the `Request-Id` does not match an unexpired pending challenge, or when the retry body does not match the update fields bound into `payloadToSign` on the initial call. + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Authentication credential not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned when the supplied email address is already associated with an `EMAIL_OTP` credential on this or another internal account. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' delete: summary: Revoke an authentication credential description: | @@ -15508,40 +15525,6 @@ components: format: date-time description: Timestamp after which this challenge is no longer valid. The signed retry must be submitted before this time. example: '2026-04-08T15:35:00Z' - InternalAccountEmailUpdateRequest: - title: Internal Account Email Update Request - description: Request body for `PATCH /internal-accounts/{id}/email`. The `email` is required on both steps of the signed-retry flow. On step 1 Grid binds it into `payloadToSign`; on step 2 the client echoes the same `email` back and Grid updates the email address used for the internal account's `EMAIL_OTP` credential. - type: object - required: - - email - properties: - email: - type: string - format: email - description: New email address to associate with the internal account's `EMAIL_OTP` credential. - example: new.email@example.com - InternalAccountEmailUpdateResponse: - title: Internal Account Email Update Response - type: object - required: - - id - - email - - updatedAt - properties: - id: - type: string - description: The id of the internal account whose email address was updated. - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - email: - type: string - format: email - description: Updated email address associated with the internal account's `EMAIL_OTP` credential. - example: new.email@example.com - updatedAt: - type: string - format: date-time - description: Timestamp when the email address was updated. - example: '2026-04-08T15:35:02Z' AuthMethodType: type: string enum: @@ -15729,7 +15712,7 @@ components: unevaluatedProperties: false AuthSignedRequestChallenge: title: Authentication Signed Request Challenge - description: 202 response returned from Embedded Wallet Auth endpoints that require a signed retry — `POST /auth/credentials` (adding an additional credential), `DELETE /auth/credentials/{id}` (revoking a credential), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, being revoked, or that issued the session being revoked). The client already knows the target resource id from the request path / body it just sent, so nothing beyond `type` is echoed in the response. + description: 202 response returned from Embedded Wallet Auth endpoints that require a signed retry — `POST /auth/credentials` (adding an additional credential), `PATCH /auth/credentials/{id}` (updating a credential), `DELETE /auth/credentials/{id}` (revoking a credential), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, updated, revoked, or that issued the session being revoked). The client already knows the target resource id from the request path / body it just sent, so nothing beyond `type` is echoed in the response. allOf: - $ref: '#/components/schemas/SignedRequestChallenge' - type: object @@ -15738,7 +15721,17 @@ components: properties: type: $ref: '#/components/schemas/AuthMethodType' - description: 'Credential type relevant to this challenge: the credential type being added (`POST /auth/credentials`), the credential type being revoked (`DELETE /auth/credentials/{id}`), or the type of credential that issued the session being revoked (`DELETE /auth/sessions/{id}`).' + description: 'Credential type relevant to this challenge: the credential type being added (`POST /auth/credentials`), updated (`PATCH /auth/credentials/{id}`), or revoked (`DELETE /auth/credentials/{id}`). For session revocation, this is the type of credential that issued the session (`DELETE /auth/sessions/{id}`).' + AuthCredentialUpdateRequest: + title: Authentication Credential Update Request + description: Partial request body for `PATCH /auth/credentials/{id}`. At least one update field must be provided. Today, only the email address on an `EMAIL_OTP` credential can be updated. On step 1 of the signed-retry flow Grid binds the submitted update fields into `payloadToSign`; on step 2 the client echoes the same fields back and Grid applies the update to the authentication credential. + type: object + properties: + email: + type: string + format: email + description: New email address to associate with the `EMAIL_OTP` credential. + example: new.email@example.com AuthCredentialVerifyRequest: type: object required: diff --git a/openapi.yaml b/openapi.yaml index 75b177fc..bf6fdd7b 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -3734,97 +3734,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Error500' - /internal-accounts/{id}/email: - patch: - summary: Update internal account email - description: | - Update the email address used for the `EMAIL_OTP` authentication credential on an Embedded Wallet internal account. - - This is a two-step signed-retry flow: - - 1. Call `PATCH /internal-accounts/{id}/email` with the request body `{ "email": "new.email@example.com" }` and no signature headers. Grid returns `202` with `payloadToSign`, `requestId`, and `expiresAt`. - - 2. Use the session API keypair of a verified authentication credential on the same internal account to build an API-key stamp over `payloadToSign`, then retry with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The retry body must carry the same `email` submitted in step 1. The signed retry returns `200` with the updated email address. - operationId: updateInternalAccountEmail - tags: - - Internal Accounts - security: - - BasicAuth: [] - parameters: - - name: id - in: path - description: The id of the internal account whose email address should be updated. - required: true - schema: - type: string - - name: Grid-Wallet-Signature - in: header - required: false - description: Full API-key stamp built over the prior `payloadToSign` with the session API keypair of a verified authentication credential on the target internal account. Required on the signed retry; ignored on the initial call. - schema: - type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - - name: Request-Id - in: header - required: false - description: The `requestId` returned in a prior `202` response, echoed back on the signed retry so the server can correlate it with the issued challenge. Required on the signed retry; must be paired with `Grid-Wallet-Signature`. - schema: - type: string - example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/InternalAccountEmailUpdateRequest' - examples: - updateEmail: - summary: Update email request (both steps) - value: - email: new.email@example.com - responses: - '200': - description: Signed retry accepted. Returns the updated email address. - content: - application/json: - schema: - $ref: '#/components/schemas/InternalAccountEmailUpdateResponse' - '202': - description: Challenge issued. The response contains `payloadToSign` plus a `requestId`. Build an API-key stamp over `payloadToSign` with the session API keypair and echo `requestId` on the retry. - content: - application/json: - schema: - $ref: '#/components/schemas/SignedRequestChallenge' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error400' - '401': - description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending email update challenge for this internal account, when the `Request-Id` does not match an unexpired pending challenge, or when the retry's `email` does not match the one bound into `payloadToSign` on the initial call. - content: - application/json: - schema: - $ref: '#/components/schemas/Error401' - '404': - description: Internal account not found - content: - application/json: - schema: - $ref: '#/components/schemas/Error404' - '409': - description: Conflict. Returned when the supplied email address is already associated with an `EMAIL_OTP` credential on this or another internal account. - content: - application/json: - schema: - $ref: '#/components/schemas/Error409' - '500': - description: Internal service error - content: - application/json: - schema: - $ref: '#/components/schemas/Error500' /auth/credentials: post: summary: Create an authentication credential @@ -4046,6 +3955,114 @@ paths: schema: $ref: '#/components/schemas/Error500' /auth/credentials/{id}: + patch: + summary: Update an authentication credential + description: | + Update mutable fields on an authentication credential for an Embedded Wallet internal account. Today this supports updating the email address used by an `EMAIL_OTP` credential. + + This is a two-step signed-retry flow: + + 1. Call `PATCH /auth/credentials/{id}` with the request body `{ "email": "new.email@example.com" }` and no signature headers. Grid returns `202` with `payloadToSign`, `requestId`, and `expiresAt`. + + 2. Use the session API keypair of a verified authentication credential on the same internal account to build an API-key stamp over `payloadToSign`, then retry with that full stamp as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The retry body must carry the same update fields submitted in step 1. The signed retry returns `200` with the updated `AuthMethod`. + operationId: updateAuthCredential + tags: + - Embedded Wallet Auth + security: + - BasicAuth: [] + parameters: + - name: id + in: path + description: The id of the authentication credential to update (the `id` field of the `AuthMethod` returned from `POST /auth/credentials`). + required: true + schema: + type: string + - name: Grid-Wallet-Signature + in: header + required: false + description: Full API-key stamp built over the prior `payloadToSign` with the session API keypair of a verified authentication credential on the same internal account. Required on the signed retry; ignored on the initial call. + schema: + type: string + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - name: Request-Id + in: header + required: false + description: The `requestId` returned in a prior `202` response, echoed back on the signed retry so the server can correlate it with the issued challenge. Required on the signed retry; must be paired with `Grid-Wallet-Signature`. + schema: + type: string + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AuthCredentialUpdateRequest' + examples: + updateEmail: + summary: Update email OTP address request (both steps) + value: + email: new.email@example.com + responses: + '200': + description: Signed retry accepted. Returns the updated authentication credential. + content: + application/json: + schema: + $ref: '#/components/schemas/AuthMethodResponse' + examples: + emailOtp: + summary: Email OTP credential updated + value: + id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001 + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + type: EMAIL_OTP + nickname: new.email@example.com + createdAt: '2026-04-08T15:30:01Z' + updatedAt: '2026-04-08T15:35:02Z' + '202': + description: Challenge issued. The response contains `payloadToSign` plus a `requestId`. Build an API-key stamp over `payloadToSign` with the session API keypair and echo `requestId` on the retry. + content: + application/json: + schema: + $ref: '#/components/schemas/AuthSignedRequestChallenge' + examples: + emailOtp: + summary: Email OTP credential update challenge + value: + type: EMAIL_OTP + payloadToSign: '{"requestId":"7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","id":"AuthMethod:019542f5-b3e7-1d02-0000-000000000001","email":"new.email@example.com","expiresAt":"2026-04-08T15:35:00Z"}' + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + expiresAt: '2026-04-08T15:35:00Z' + '400': + description: Bad request. Also returned when attempting to update a field that is not supported for the target credential type. + content: + application/json: + schema: + $ref: '#/components/schemas/Error400' + '401': + description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending credential update challenge, when the `Request-Id` does not match an unexpired pending challenge, or when the retry body does not match the update fields bound into `payloadToSign` on the initial call. + content: + application/json: + schema: + $ref: '#/components/schemas/Error401' + '404': + description: Authentication credential not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error404' + '409': + description: Conflict. Returned when the supplied email address is already associated with an `EMAIL_OTP` credential on this or another internal account. + content: + application/json: + schema: + $ref: '#/components/schemas/Error409' + '500': + description: Internal service error + content: + application/json: + schema: + $ref: '#/components/schemas/Error500' delete: summary: Revoke an authentication credential description: | @@ -15508,40 +15525,6 @@ components: format: date-time description: Timestamp after which this challenge is no longer valid. The signed retry must be submitted before this time. example: '2026-04-08T15:35:00Z' - InternalAccountEmailUpdateRequest: - title: Internal Account Email Update Request - description: Request body for `PATCH /internal-accounts/{id}/email`. The `email` is required on both steps of the signed-retry flow. On step 1 Grid binds it into `payloadToSign`; on step 2 the client echoes the same `email` back and Grid updates the email address used for the internal account's `EMAIL_OTP` credential. - type: object - required: - - email - properties: - email: - type: string - format: email - description: New email address to associate with the internal account's `EMAIL_OTP` credential. - example: new.email@example.com - InternalAccountEmailUpdateResponse: - title: Internal Account Email Update Response - type: object - required: - - id - - email - - updatedAt - properties: - id: - type: string - description: The id of the internal account whose email address was updated. - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - email: - type: string - format: email - description: Updated email address associated with the internal account's `EMAIL_OTP` credential. - example: new.email@example.com - updatedAt: - type: string - format: date-time - description: Timestamp when the email address was updated. - example: '2026-04-08T15:35:02Z' AuthMethodType: type: string enum: @@ -15729,7 +15712,7 @@ components: unevaluatedProperties: false AuthSignedRequestChallenge: title: Authentication Signed Request Challenge - description: 202 response returned from Embedded Wallet Auth endpoints that require a signed retry — `POST /auth/credentials` (adding an additional credential), `DELETE /auth/credentials/{id}` (revoking a credential), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, being revoked, or that issued the session being revoked). The client already knows the target resource id from the request path / body it just sent, so nothing beyond `type` is echoed in the response. + description: 202 response returned from Embedded Wallet Auth endpoints that require a signed retry — `POST /auth/credentials` (adding an additional credential), `PATCH /auth/credentials/{id}` (updating a credential), `DELETE /auth/credentials/{id}` (revoking a credential), and `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing fields from `SignedRequestChallenge` plus the `type` of the authentication credential involved (being added, updated, revoked, or that issued the session being revoked). The client already knows the target resource id from the request path / body it just sent, so nothing beyond `type` is echoed in the response. allOf: - $ref: '#/components/schemas/SignedRequestChallenge' - type: object @@ -15738,7 +15721,17 @@ components: properties: type: $ref: '#/components/schemas/AuthMethodType' - description: 'Credential type relevant to this challenge: the credential type being added (`POST /auth/credentials`), the credential type being revoked (`DELETE /auth/credentials/{id}`), or the type of credential that issued the session being revoked (`DELETE /auth/sessions/{id}`).' + description: 'Credential type relevant to this challenge: the credential type being added (`POST /auth/credentials`), updated (`PATCH /auth/credentials/{id}`), or revoked (`DELETE /auth/credentials/{id}`). For session revocation, this is the type of credential that issued the session (`DELETE /auth/sessions/{id}`).' + AuthCredentialUpdateRequest: + title: Authentication Credential Update Request + description: Partial request body for `PATCH /auth/credentials/{id}`. At least one update field must be provided. Today, only the email address on an `EMAIL_OTP` credential can be updated. On step 1 of the signed-retry flow Grid binds the submitted update fields into `payloadToSign`; on step 2 the client echoes the same fields back and Grid applies the update to the authentication credential. + type: object + properties: + email: + type: string + format: email + description: New email address to associate with the `EMAIL_OTP` credential. + example: new.email@example.com AuthCredentialVerifyRequest: type: object required: diff --git a/openapi/components/schemas/auth/AuthCredentialUpdateRequest.yaml b/openapi/components/schemas/auth/AuthCredentialUpdateRequest.yaml new file mode 100644 index 00000000..0c9d23b1 --- /dev/null +++ b/openapi/components/schemas/auth/AuthCredentialUpdateRequest.yaml @@ -0,0 +1,15 @@ +title: Authentication Credential Update Request +description: >- + Partial request body for `PATCH /auth/credentials/{id}`. At least one + update field must be provided. Today, only the email address on an + `EMAIL_OTP` credential can be updated. On step 1 of the signed-retry flow + Grid binds the submitted update fields into `payloadToSign`; on step 2 the + client echoes the same fields back and Grid applies the update to the + authentication credential. +type: object +properties: + email: + type: string + format: email + description: New email address to associate with the `EMAIL_OTP` credential. + example: new.email@example.com diff --git a/openapi/components/schemas/auth/AuthSignedRequestChallenge.yaml b/openapi/components/schemas/auth/AuthSignedRequestChallenge.yaml index 605aed4b..e7fb59c1 100644 --- a/openapi/components/schemas/auth/AuthSignedRequestChallenge.yaml +++ b/openapi/components/schemas/auth/AuthSignedRequestChallenge.yaml @@ -2,13 +2,14 @@ title: Authentication Signed Request Challenge description: >- 202 response returned from Embedded Wallet Auth endpoints that require a signed retry — `POST /auth/credentials` (adding an additional - credential), `DELETE /auth/credentials/{id}` (revoking a credential), - and `DELETE /auth/sessions/{id}` (revoking a session). Carries the - signing fields from `SignedRequestChallenge` plus the `type` of the - authentication credential involved (being added, being revoked, or - that issued the session being revoked). The client already knows the - target resource id from the request path / body it just sent, so - nothing beyond `type` is echoed in the response. + credential), `PATCH /auth/credentials/{id}` (updating a credential), + `DELETE /auth/credentials/{id}` (revoking a credential), and + `DELETE /auth/sessions/{id}` (revoking a session). Carries the signing + fields from `SignedRequestChallenge` plus the `type` of the authentication + credential involved (being added, updated, revoked, or that issued the + session being revoked). The client already knows the target resource id + from the request path / body it just sent, so nothing beyond `type` is + echoed in the response. allOf: - $ref: ../common/SignedRequestChallenge.yaml - type: object @@ -19,7 +20,8 @@ allOf: $ref: ./AuthMethodType.yaml description: >- Credential type relevant to this challenge: the credential type - being added (`POST /auth/credentials`), the credential type - being revoked (`DELETE /auth/credentials/{id}`), or the type of - credential that issued the session being revoked + being added (`POST /auth/credentials`), updated + (`PATCH /auth/credentials/{id}`), or revoked + (`DELETE /auth/credentials/{id}`). For session revocation, this is + the type of credential that issued the session (`DELETE /auth/sessions/{id}`). diff --git a/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateRequest.yaml b/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateRequest.yaml deleted file mode 100644 index 3ea8aa7e..00000000 --- a/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateRequest.yaml +++ /dev/null @@ -1,16 +0,0 @@ -title: Internal Account Email Update Request -description: >- - Request body for `PATCH /internal-accounts/{id}/email`. The `email` - is required on both steps of the signed-retry flow. On step 1 Grid - binds it into `payloadToSign`; on step 2 the client echoes the same - `email` back and Grid updates the email address used for the - internal account's `EMAIL_OTP` credential. -type: object -required: - - email -properties: - email: - type: string - format: email - description: New email address to associate with the internal account's `EMAIL_OTP` credential. - example: new.email@example.com diff --git a/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateResponse.yaml b/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateResponse.yaml deleted file mode 100644 index 069ad0cc..00000000 --- a/openapi/components/schemas/internal_accounts/InternalAccountEmailUpdateResponse.yaml +++ /dev/null @@ -1,21 +0,0 @@ -title: Internal Account Email Update Response -type: object -required: - - id - - email - - updatedAt -properties: - id: - type: string - description: The id of the internal account whose email address was updated. - example: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 - email: - type: string - format: email - description: Updated email address associated with the internal account's `EMAIL_OTP` credential. - example: new.email@example.com - updatedAt: - type: string - format: date-time - description: Timestamp when the email address was updated. - example: '2026-04-08T15:35:02Z' diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index d2ae90bf..771b4131 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -201,8 +201,6 @@ paths: $ref: paths/tokens/tokens_{tokenId}.yaml /internal-accounts/{id}/export: $ref: paths/internal_accounts/internal_accounts_{id}_export.yaml - /internal-accounts/{id}/email: - $ref: paths/internal_accounts/internal_accounts_{id}_email.yaml /auth/credentials: $ref: paths/auth/auth_credentials.yaml /auth/credentials/{id}: diff --git a/openapi/paths/auth/auth_credentials_{id}.yaml b/openapi/paths/auth/auth_credentials_{id}.yaml index a763eddc..b77d8e3e 100644 --- a/openapi/paths/auth/auth_credentials_{id}.yaml +++ b/openapi/paths/auth/auth_credentials_{id}.yaml @@ -1,3 +1,150 @@ +patch: + summary: Update an authentication credential + description: > + Update mutable fields on an authentication credential for an Embedded + Wallet internal account. Today this supports updating the email address + used by an `EMAIL_OTP` credential. + + + This is a two-step signed-retry flow: + + + 1. Call `PATCH /auth/credentials/{id}` with the request body + `{ "email": "new.email@example.com" }` and no signature headers. + Grid returns `202` with `payloadToSign`, `requestId`, and + `expiresAt`. + + + 2. Use the session API keypair of a verified authentication credential + on the same internal account to build an API-key stamp over + `payloadToSign`, then retry with that full stamp as the + `Grid-Wallet-Signature` header and the `requestId` echoed back as + the `Request-Id` header. The retry body must carry the same update + fields submitted in step 1. The signed retry returns `200` with the + updated `AuthMethod`. + operationId: updateAuthCredential + tags: + - Embedded Wallet Auth + security: + - BasicAuth: [] + parameters: + - name: id + in: path + description: >- + The id of the authentication credential to update (the `id` + field of the `AuthMethod` returned from + `POST /auth/credentials`). + required: true + schema: + type: string + - name: Grid-Wallet-Signature + in: header + required: false + description: >- + Full API-key stamp built over the prior `payloadToSign` with + the session API keypair of a verified authentication credential + on the same internal account. Required on the signed retry; + ignored on the initial call. + schema: + type: string + example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ + - name: Request-Id + in: header + required: false + description: >- + The `requestId` returned in a prior `202` response, echoed back + on the signed retry so the server can correlate it with the + issued challenge. Required on the signed retry; must be paired + with `Grid-Wallet-Signature`. + schema: + type: string + example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../components/schemas/auth/AuthCredentialUpdateRequest.yaml + examples: + updateEmail: + summary: Update email OTP address request (both steps) + value: + email: new.email@example.com + responses: + '200': + description: Signed retry accepted. Returns the updated authentication credential. + content: + application/json: + schema: + $ref: ../../components/schemas/auth/AuthMethodResponse.yaml + examples: + emailOtp: + summary: Email OTP credential updated + value: + id: AuthMethod:019542f5-b3e7-1d02-0000-000000000001 + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + type: EMAIL_OTP + nickname: new.email@example.com + createdAt: '2026-04-08T15:30:01Z' + updatedAt: '2026-04-08T15:35:02Z' + '202': + description: >- + Challenge issued. The response contains `payloadToSign` plus a + `requestId`. Build an API-key stamp over `payloadToSign` with + the session API keypair and echo `requestId` on the retry. + content: + application/json: + schema: + $ref: ../../components/schemas/auth/AuthSignedRequestChallenge.yaml + examples: + emailOtp: + summary: Email OTP credential update challenge + value: + type: EMAIL_OTP + payloadToSign: '{"requestId":"7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21","id":"AuthMethod:019542f5-b3e7-1d02-0000-000000000001","email":"new.email@example.com","expiresAt":"2026-04-08T15:35:00Z"}' + requestId: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 + expiresAt: '2026-04-08T15:35:00Z' + '400': + description: >- + Bad request. Also returned when attempting to update a field that is + not supported for the target credential type. + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error400.yaml + '401': + description: >- + Unauthorized. Returned when the provided `Grid-Wallet-Signature` + is missing, malformed, or does not match a pending credential + update challenge, when the `Request-Id` does not match an unexpired + pending challenge, or when the retry body does not match the update + fields bound into `payloadToSign` on the initial call. + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error401.yaml + '404': + description: Authentication credential not found + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error404.yaml + '409': + description: >- + Conflict. Returned when the supplied email address is already + associated with an `EMAIL_OTP` credential on this or another + internal account. + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error409.yaml + '500': + description: Internal service error + content: + application/json: + schema: + $ref: ../../components/schemas/errors/Error500.yaml + delete: summary: Revoke an authentication credential description: > diff --git a/openapi/paths/internal_accounts/internal_accounts_{id}_email.yaml b/openapi/paths/internal_accounts/internal_accounts_{id}_email.yaml deleted file mode 100644 index 709e0eb9..00000000 --- a/openapi/paths/internal_accounts/internal_accounts_{id}_email.yaml +++ /dev/null @@ -1,123 +0,0 @@ -patch: - summary: Update internal account email - description: > - Update the email address used for the `EMAIL_OTP` authentication - credential on an Embedded Wallet internal account. - - - This is a two-step signed-retry flow: - - - 1. Call `PATCH /internal-accounts/{id}/email` with the request body - `{ "email": "new.email@example.com" }` and no signature headers. - Grid returns `202` with `payloadToSign`, `requestId`, and - `expiresAt`. - - - 2. Use the session API keypair of a verified authentication credential - on the same internal account to build an API-key stamp over - `payloadToSign`, then retry with that full stamp as the - `Grid-Wallet-Signature` header and the `requestId` echoed back as - the `Request-Id` header. The retry body must carry the same `email` - submitted in step 1. The signed retry returns `200` with the updated - email address. - operationId: updateInternalAccountEmail - tags: - - Internal Accounts - security: - - BasicAuth: [] - parameters: - - name: id - in: path - description: The id of the internal account whose email address should be updated. - required: true - schema: - type: string - - name: Grid-Wallet-Signature - in: header - required: false - description: >- - Full API-key stamp built over the prior `payloadToSign` with - the session API keypair of a verified authentication credential - on the target internal account. Required on the signed retry; - ignored on the initial call. - schema: - type: string - example: eyJwdWJsaWNLZXkiOiIwMmExYjIuLi4iLCJzaWduYXR1cmUiOiIzMDQ1MDIyMTAwLi4uIiwic2NoZW1lIjoiUDI1Nl9FQ0RTQV9TSEEyNTYifQ - - name: Request-Id - in: header - required: false - description: >- - The `requestId` returned in a prior `202` response, echoed back - on the signed retry so the server can correlate it with the - issued challenge. Required on the signed retry; must be paired - with `Grid-Wallet-Signature`. - schema: - type: string - example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21 - requestBody: - required: true - content: - application/json: - schema: - $ref: ../../components/schemas/internal_accounts/InternalAccountEmailUpdateRequest.yaml - examples: - updateEmail: - summary: Update email request (both steps) - value: - email: new.email@example.com - responses: - '200': - description: Signed retry accepted. Returns the updated email address. - content: - application/json: - schema: - $ref: ../../components/schemas/internal_accounts/InternalAccountEmailUpdateResponse.yaml - '202': - description: >- - Challenge issued. The response contains `payloadToSign` plus a - `requestId`. Build an API-key stamp over `payloadToSign` with - the session API keypair and echo `requestId` on the retry. - content: - application/json: - schema: - $ref: ../../components/schemas/common/SignedRequestChallenge.yaml - '400': - description: Bad request - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error400.yaml - '401': - description: >- - Unauthorized. Returned when the provided `Grid-Wallet-Signature` - is missing, malformed, or does not match a pending email update - challenge for this internal account, when the `Request-Id` does - not match an unexpired pending challenge, or when the retry's - `email` does not match the one bound into `payloadToSign` on - the initial call. - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error401.yaml - '409': - description: >- - Conflict. Returned when the supplied email address is already - associated with an `EMAIL_OTP` credential on this or another - internal account. - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error409.yaml - '404': - description: Internal account not found - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error404.yaml - '500': - description: Internal service error - content: - application/json: - schema: - $ref: ../../components/schemas/errors/Error500.yaml