diff --git a/Makefile b/Makefile
index 9f71096e..58ca7e26 100644
--- a/Makefile
+++ b/Makefile
@@ -11,10 +11,18 @@ dev:
install:
cd scripts/openapi-gen && npm install
-# Default MDX generation target
+# Generate MDX for all APIs (main + auth proxy).
+# Requires both swagger files to be present in the repo root — see README for copy commands.
gen: mintlify-check
npx ts-node scripts/openapi-gen/swagger-to-openapi.ts
cd scripts/openapi-gen && npx ts-node openapi-gen.ts --file=openapi.json --generate-mdx
+ npx ts-node scripts/openapi-gen/swagger-to-openapi.ts --service=auth-proxy
+ cd scripts/openapi-gen && npx ts-node openapi-gen.ts \
+ --file=proxy_api_openapi.json \
+ --generate-mdx \
+ --mdx-output-dir=api-reference/auth-proxy \
+ --nav-group="Auth Proxy" \
+ --auth-proxy
# Run the OpenAPI generator (allows custom ARGS)
openapi-gen: mintlify-check
diff --git a/README.md b/README.md
index ef84138f..53f93fcf 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ We provide the following Make targets to generate API reference content:
- `make install`: Install dependencies needed to run OpenAPI commands
- `make openapi-gen`: Run the OpenAPI generator CLI with custom arguments. See [scripts/openapi-gen/README.md](scripts/openapi-gen/README.md) for details.
-- `make gen`: Default MDX generation for API reference under `api-reference/`.
+- `make gen`: MDX generation for all API references (main API + Auth Proxy) under `api-reference/`.
- `make tags`: Generate the endpoint-tags MDX snippet at `snippets/data/endpoint-tags.mdx`.
- `make mintlify-check`: This attempts to compare the locally installed version of mintlify cli against the latest published version on NPM. If the local version is behind this will attempt to update the version to the latest. This command gets run before `make gen`, `make openapi-gen`, and `make tags` to prevent parsing errors.
@@ -58,7 +58,7 @@ These commands require Node.js and `ts-node`.
### Mono Releases & Updating API Reference
-1. Checkout the specific [Mono Release tag](https://github.com/tkhq/mono/tags) and copy over [public_api.swagger.json](https://github.com/tkhq/mono/blob/main/src/js/internal/http/src/__internal__/services/coordinator/public/v1/public_api.swagger.json) to docs
+1. Checkout the specific [Mono Release tag](https://github.com/tkhq/mono/tags) and copy both swagger files `public_api.swagger.json` and `proxy_api.swagger.json` into the docs
- `git checkout {latest_release_tag}` i.e. `v2025.12.1`
- `make -C proto sync/docs`
2. Run `make gen`
diff --git a/api-reference/auth-proxy/account.mdx b/api-reference/auth-proxy/account.mdx
new file mode 100644
index 00000000..57ccb30c
--- /dev/null
+++ b/api-reference/auth-proxy/account.mdx
@@ -0,0 +1,78 @@
+---
+title: "Get Account"
+description: "Return organization id associated with a given phone number, email, public key, credential ID or OIDC token."
+---
+
+import { H3Bordered } from "/snippets/h3-bordered.mdx";
+import { NestedParam } from "/snippets/nested-param.mdx";
+
+
+
+
+
POST
+
https://authproxy.turnkey.com/v1/account
+
+
+
+
+
+
+
+ Your Auth Proxy config ID, found in **Dashboard → AUTH**. See [Auth Proxy reference](/reference/auth-proxy) for setup.
+
+
+
+
+
+
+Specifies the type of filter to apply, i.e 'CREDENTIAL_ID', 'NAME', 'USERNAME', 'EMAIL', 'PHONE_NUMBER', 'OIDC_TOKEN' or 'PUBLIC_KEY'
+
+
+
+
+The value of the filter to apply for the specified type. For example, a specific email or name string.
+
+
+
+
+Signed JWT containing a unique id, expiry, verification type, contact. Used to verify access to PII (email/phone number) when filter_type is 'EMAIL' or 'PHONE_NUMBER'.
+
+
+
+
+OIDC token to verify access to PII (email/phone number) when filter_type is 'EMAIL' or 'PHONE_NUMBER'. Needed for social linking when verification_token is not available.
+
+
+
+
+A successful response returns the following fields:
+
+organizationId field
+
+
+
+```bash title="cURL"
+curl --request POST \
+ --url https://authproxy.turnkey.com/v1/account \
+ --header 'Accept: application/json' \
+ --header 'Content-Type: application/json' \
+ --header "X-Auth-Proxy-Config-Id: (see Authorizations)" \
+ --data '{
+ "filterType": "",
+ "filterValue": "",
+ "verificationToken": "",
+ "oidcToken": ""
+}'
+```
+
+
+
+
+
+```json 200
+{
+ "organizationId": ""
+}
+```
+
+
diff --git a/api-reference/auth-proxy/oauth-login.mdx b/api-reference/auth-proxy/oauth-login.mdx
new file mode 100644
index 00000000..ac9d0160
--- /dev/null
+++ b/api-reference/auth-proxy/oauth-login.mdx
@@ -0,0 +1,78 @@
+---
+title: "OAuth Login"
+description: "Login using an OIDC token and public key."
+---
+
+import { H3Bordered } from "/snippets/h3-bordered.mdx";
+import { NestedParam } from "/snippets/nested-param.mdx";
+
+
+
+
+
POST
+
https://authproxy.turnkey.com/v1/oauth_login
+
+
+
+
+
+
+
+ Your Auth Proxy config ID, found in **Dashboard → AUTH**. See [Auth Proxy reference](/reference/auth-proxy) for setup.
+
+
+
+
+
+
+Base64 encoded OIDC token
+
+
+
+
+Client-side public key generated by the user, which will be conditionally added to org data based on the validity of the oidc token associated with this request
+
+
+
+
+Invalidate all other previously generated Login API keys
+
+
+
+
+Unique identifier for a given Organization. If provided, this organization id will be used directly. If omitted, uses the OIDC token to look up the associated organization id.
+
+
+
+
+A successful response returns the following fields:
+
+Signed JWT containing an expiry, public key, session type, user id, and organization id
+
+
+
+```bash title="cURL"
+curl --request POST \
+ --url https://authproxy.turnkey.com/v1/oauth_login \
+ --header 'Accept: application/json' \
+ --header 'Content-Type: application/json' \
+ --header "X-Auth-Proxy-Config-Id: (see Authorizations)" \
+ --data '{
+ "oidcToken": "",
+ "publicKey": "",
+ "invalidateExisting": "",
+ "organizationId": ""
+}'
+```
+
+
+
+
+
+```json 200
+{
+ "session": ""
+}
+```
+
+
diff --git a/api-reference/auth-proxy/oauth2-authenticate.mdx b/api-reference/auth-proxy/oauth2-authenticate.mdx
new file mode 100644
index 00000000..5787f9cb
--- /dev/null
+++ b/api-reference/auth-proxy/oauth2-authenticate.mdx
@@ -0,0 +1,90 @@
+---
+title: "OAuth 2.0 Authenticate"
+description: "Authenticate with an OAuth 2.0 provider and receive an OIDC token issued by Turnkey in response."
+---
+
+import { H3Bordered } from "/snippets/h3-bordered.mdx";
+import { NestedParam } from "/snippets/nested-param.mdx";
+
+
+
+
+
POST
+
https://authproxy.turnkey.com/v1/oauth2_authenticate
+
+
+
+
+
+
+
+ Your Auth Proxy config ID, found in **Dashboard → AUTH**. See [Auth Proxy reference](/reference/auth-proxy) for setup.
+
+
+
+
+
+
+Enum options: `OAUTH2_PROVIDER_X`, `OAUTH2_PROVIDER_DISCORD`
+
+
+
+
+The auth_code provided by the OAuth 2.0 to the end user to be exchanged for a Bearer token in the OAuth 2.0 flow
+
+
+
+
+The URI the user is redirected to after they have authenticated with the OAuth 2.0 provider
+
+
+
+
+The code verifier used by OAuth 2.0 PKCE providers
+
+
+
+
+An optional nonce used by the client to prevent replay/substitution of an ID token
+
+
+
+
+The client ID registered with the OAuth 2.0 provider
+
+
+
+
+A successful response returns the following fields:
+
+A Turnkey issued OIDC token to be used with the LoginWithOAuth activity
+
+
+
+```bash title="cURL"
+curl --request POST \
+ --url https://authproxy.turnkey.com/v1/oauth2_authenticate \
+ --header 'Accept: application/json' \
+ --header 'Content-Type: application/json' \
+ --header "X-Auth-Proxy-Config-Id: (see Authorizations)" \
+ --data '{
+ "provider": "",
+ "authCode": "",
+ "redirectUri": "",
+ "codeVerifier": "",
+ "nonce": "",
+ "clientId": ""
+}'
+```
+
+
+
+
+
+```json 200
+{
+ "oidcToken": ""
+}
+```
+
+
diff --git a/api-reference/auth-proxy/otp-init.mdx b/api-reference/auth-proxy/otp-init.mdx
new file mode 100644
index 00000000..b7993f71
--- /dev/null
+++ b/api-reference/auth-proxy/otp-init.mdx
@@ -0,0 +1,68 @@
+---
+title: "Init OTP"
+description: "Start a new OTP flow and return a new OTP flow ID."
+---
+
+import { H3Bordered } from "/snippets/h3-bordered.mdx";
+import { NestedParam } from "/snippets/nested-param.mdx";
+
+
+
+
+
POST
+
https://authproxy.turnkey.com/v1/otp_init_v2
+
+
+
+
+
+
+
+ Your Auth Proxy config ID, found in **Dashboard → AUTH**. See [Auth Proxy reference](/reference/auth-proxy) for setup.
+
+
+
+
+
+
+Enum to specify whether to send OTP code via SMS or email
+
+
+
+
+Email or phone number to send the OTP code to
+
+
+
+
+A successful response returns the following fields:
+
+Unique identifier for an OTP flow.
+Signed bundle containing a target encryption key to use when submitting OTP codes.
+
+
+
+```bash title="cURL"
+curl --request POST \
+ --url https://authproxy.turnkey.com/v1/otp_init_v2 \
+ --header 'Accept: application/json' \
+ --header 'Content-Type: application/json' \
+ --header "X-Auth-Proxy-Config-Id: (see Authorizations)" \
+ --data '{
+ "otpType": "",
+ "contact": ""
+}'
+```
+
+
+
+
+
+```json 200
+{
+ "otpId": "",
+ "otpEncryptionTargetBundle": ""
+}
+```
+
+
diff --git a/api-reference/auth-proxy/otp-login.mdx b/api-reference/auth-proxy/otp-login.mdx
new file mode 100644
index 00000000..2c603d9a
--- /dev/null
+++ b/api-reference/auth-proxy/otp-login.mdx
@@ -0,0 +1,104 @@
+---
+title: "OTP Login"
+description: "Login using an existing OTP Verification Token and a client-side signature. The signature's public key must match the public key contained within the OTP Verification Token."
+---
+
+import { H3Bordered } from "/snippets/h3-bordered.mdx";
+import { NestedParam } from "/snippets/nested-param.mdx";
+
+
+
+
+
POST
+
https://authproxy.turnkey.com/v1/otp_login_v2
+
+
+
+
+
+
+
+ Your Auth Proxy config ID, found in **Dashboard → AUTH**. See [Auth Proxy reference](/reference/auth-proxy) for setup.
+
+
+
+
+
+
+Session containing a unique id, expiry, verification type, contact. Verification status of a user is updated when the token is consumed (in OTP_LOGIN requests)
+
+
+
+
+Client-side public key generated by the user, used as the session public key upon successful login.
+
+
+
+ clientSignature field
+
+
+ The public component of a cryptographic key pair used to create the signature.
+
+
+
+ Enum options: `CLIENT_SIGNATURE_SCHEME_API_P256`
+
+
+
+ The message that was signed.
+
+
+
+ The cryptographic signature over the message.
+
+
+
+
+
+Invalidate all other previously generated Login sessions
+
+
+
+
+Unique identifier for a given Organization. If provided, this organization id will be used directly. If omitted, uses the verification token to look up the verified sub-organization based on the contact and verification type.
+
+
+
+
+A successful response returns the following fields:
+
+Session containing an expiry, public key, session type, user id, and organization id
+
+
+
+```bash title="cURL"
+curl --request POST \
+ --url https://authproxy.turnkey.com/v1/otp_login_v2 \
+ --header 'Accept: application/json' \
+ --header 'Content-Type: application/json' \
+ --header "X-Auth-Proxy-Config-Id: (see Authorizations)" \
+ --data '{
+ "verificationToken": "",
+ "publicKey": "",
+ "clientSignature": {
+ "publicKey": "",
+ "scheme": "",
+ "message": "",
+ "signature": ""
+ },
+ "invalidateExisting": "",
+ "organizationId": ""
+}'
+```
+
+
+
+
+
+```json 200
+{
+ "session": ""
+}
+```
+
+
diff --git a/api-reference/auth-proxy/otp-verify.mdx b/api-reference/auth-proxy/otp-verify.mdx
new file mode 100644
index 00000000..a4386a2e
--- /dev/null
+++ b/api-reference/auth-proxy/otp-verify.mdx
@@ -0,0 +1,66 @@
+---
+title: "Verify OTP"
+description: "Verify the OTP code previously sent to the user's contact and return a verification token."
+---
+
+import { H3Bordered } from "/snippets/h3-bordered.mdx";
+import { NestedParam } from "/snippets/nested-param.mdx";
+
+
+
+
+
POST
+
https://authproxy.turnkey.com/v1/otp_verify_v2
+
+
+
+
+
+
+
+ Your Auth Proxy config ID, found in **Dashboard → AUTH**. See [Auth Proxy reference](/reference/auth-proxy) for setup.
+
+
+
+
+
+
+ID representing the result of an init OTP activity.
+
+
+
+
+Encrypted bundle containing the OTP code and a client-generated public key. Turnkey's secure enclaves will decrypt this bundle, verify the OTP code, and issue a new Verification Token. Encrypted using the target encryption key provided in the INIT_OTP activity result.
+
+
+
+
+A successful response returns the following fields:
+
+Verification Token containing a unique id, expiry, verification type, contact signed by Turnkey's enclaves. Verification status of a user is updated when the token is consumed (in OTP_LOGIN requests)
+
+
+
+```bash title="cURL"
+curl --request POST \
+ --url https://authproxy.turnkey.com/v1/otp_verify_v2 \
+ --header 'Accept: application/json' \
+ --header 'Content-Type: application/json' \
+ --header "X-Auth-Proxy-Config-Id: (see Authorizations)" \
+ --data '{
+ "otpId": "",
+ "encryptedOtpBundle": ""
+}'
+```
+
+
+
+
+
+```json 200
+{
+ "verificationToken": ""
+}
+```
+
+
diff --git a/api-reference/auth-proxy/signup.mdx b/api-reference/auth-proxy/signup.mdx
new file mode 100644
index 00000000..58d6228b
--- /dev/null
+++ b/api-reference/auth-proxy/signup.mdx
@@ -0,0 +1,330 @@
+---
+title: "Signup"
+description: "Onboard a new user."
+---
+
+import { H3Bordered } from "/snippets/h3-bordered.mdx";
+import { NestedParam } from "/snippets/nested-param.mdx";
+
+
+
+
+
POST
+
https://authproxy.turnkey.com/v1/signup_v2
+
+
+
+
+
+
+
+ Your Auth Proxy config ID, found in **Dashboard → AUTH**. See [Auth Proxy reference](/reference/auth-proxy) for setup.
+
+
+
+
+
+
+userEmail field
+
+
+
+
+userPhoneNumber field
+
+
+
+
+userTag field
+
+
+
+
+userName field
+
+
+
+
+organizationName field
+
+
+
+
+verificationToken field
+
+
+
+ A list of API Key parameters. This field, if not needed, should be an empty array in your request body.
+
+
+ Human-readable name for an API Key.
+
+
+
+ The public component of a cryptographic key pair used to sign messages and transactions.
+
+
+
+ Enum options: `API_KEY_CURVE_P256`, `API_KEY_CURVE_SECP256K1`, `API_KEY_CURVE_ED25519`
+
+
+
+ Optional window (in seconds) indicating how long the API Key should last.
+
+
+
+
+ A list of Authenticator parameters. This field, if not needed, should be an empty array in your request body.
+
+
+ Human-readable name for an Authenticator.
+
+
+
+ Challenge presented for authentication purposes.
+
+
+
+ attestation field
+
+
+ The cbor encoded then base64 url encoded id of the credential.
+
+
+
+ A base64 url encoded payload containing metadata about the signing context and the challenge.
+
+
+
+ A base64 url encoded payload containing authenticator data and any attestation the webauthn provider chooses.
+
+
+
+ Enum options: `AUTHENTICATOR_TRANSPORT_BLE`, `AUTHENTICATOR_TRANSPORT_INTERNAL`, `AUTHENTICATOR_TRANSPORT_NFC`, `AUTHENTICATOR_TRANSPORT_USB`, `AUTHENTICATOR_TRANSPORT_HYBRID`
+
+
+
+
+
+
+ A list of Oauth providers. This field, if not needed, should be an empty array in your request body.
+
+
+ Human-readable name to identify a Provider.
+
+
+
+ Base64 encoded OIDC token
+
+
+
+ oidcClaims field
+
+
+ The issuer identifier from the OIDC token (iss claim)
+
+
+
+ The subject identifier from the OIDC token (sub claim)
+
+
+
+ The audience from the OIDC token (aud claim)
+
+
+
+
+
+
+ wallet field
+
+
+ Human-readable name for a Wallet.
+
+
+
+ A list of wallet Accounts. This field, if not needed, should be an empty array in your request body.
+
+
+ Enum options: `CURVE_SECP256K1`, `CURVE_ED25519`, `CURVE_P256`
+
+
+
+ Enum options: `PATH_FORMAT_BIP32`
+
+
+
+ Path used to generate a wallet Account.
+
+
+
+ Enum options: `ADDRESS_FORMAT_UNCOMPRESSED`, `ADDRESS_FORMAT_COMPRESSED`, `ADDRESS_FORMAT_ETHEREUM`, `ADDRESS_FORMAT_SOLANA`, `ADDRESS_FORMAT_COSMOS`, `ADDRESS_FORMAT_TRON`, `ADDRESS_FORMAT_SUI`, `ADDRESS_FORMAT_APTOS`, `ADDRESS_FORMAT_BITCOIN_MAINNET_P2PKH`, `ADDRESS_FORMAT_BITCOIN_MAINNET_P2SH`, `ADDRESS_FORMAT_BITCOIN_MAINNET_P2WPKH`, `ADDRESS_FORMAT_BITCOIN_MAINNET_P2WSH`, `ADDRESS_FORMAT_BITCOIN_MAINNET_P2TR`, `ADDRESS_FORMAT_BITCOIN_TESTNET_P2PKH`, `ADDRESS_FORMAT_BITCOIN_TESTNET_P2SH`, `ADDRESS_FORMAT_BITCOIN_TESTNET_P2WPKH`, `ADDRESS_FORMAT_BITCOIN_TESTNET_P2WSH`, `ADDRESS_FORMAT_BITCOIN_TESTNET_P2TR`, `ADDRESS_FORMAT_BITCOIN_SIGNET_P2PKH`, `ADDRESS_FORMAT_BITCOIN_SIGNET_P2SH`, `ADDRESS_FORMAT_BITCOIN_SIGNET_P2WPKH`, `ADDRESS_FORMAT_BITCOIN_SIGNET_P2WSH`, `ADDRESS_FORMAT_BITCOIN_SIGNET_P2TR`, `ADDRESS_FORMAT_BITCOIN_REGTEST_P2PKH`, `ADDRESS_FORMAT_BITCOIN_REGTEST_P2SH`, `ADDRESS_FORMAT_BITCOIN_REGTEST_P2WPKH`, `ADDRESS_FORMAT_BITCOIN_REGTEST_P2WSH`, `ADDRESS_FORMAT_BITCOIN_REGTEST_P2TR`, `ADDRESS_FORMAT_SEI`, `ADDRESS_FORMAT_XLM`, `ADDRESS_FORMAT_DOGE_MAINNET`, `ADDRESS_FORMAT_DOGE_TESTNET`, `ADDRESS_FORMAT_TON_V3R2`, `ADDRESS_FORMAT_TON_V4R2`, `ADDRESS_FORMAT_TON_V5R1`, `ADDRESS_FORMAT_XRP`, `ADDRESS_FORMAT_SPARK_MAINNET`, `ADDRESS_FORMAT_SPARK_REGTEST`
+
+
+
+
+ Length of mnemonic to generate the Wallet seed. Defaults to 12. Accepted values: 12, 15, 18, 21, 24.
+
+
+
+
+ clientSignature field
+
+
+ The public component of a cryptographic key pair used to create the signature.
+
+
+
+ Enum options: `CLIENT_SIGNATURE_SCHEME_API_P256`
+
+
+
+ The message that was signed.
+
+
+
+ The cryptographic signature over the message.
+
+
+
+
+
+A successful response returns the following fields:
+
+organizationId field
+
+ wallet field
+
+
+walletId field
+
+
+ A list of account addresses.
+
+
+item field
+
+
+
+
+
+
+
+Root user ID created for this sub-organization
+
+ A list of App Proofs generated by enclaves during activity execution, providing verifiable attestations of performed operations.
+
+
+scheme field
+
+Enum options: `SIGNATURE_SCHEME_EPHEMERAL_KEY_P256`
+
+
+
+Ephemeral public key.
+
+
+JSON serialized AppProofPayload.
+
+
+Signature over hashed proof_payload.
+
+
+
+
+
+
+
+```bash title="cURL"
+curl --request POST \
+ --url https://authproxy.turnkey.com/v1/signup_v2 \
+ --header 'Accept: application/json' \
+ --header 'Content-Type: application/json' \
+ --header "X-Auth-Proxy-Config-Id: (see Authorizations)" \
+ --data '{
+ "userEmail": "",
+ "userPhoneNumber": "",
+ "userTag": "",
+ "userName": "",
+ "organizationName": "",
+ "verificationToken": "",
+ "apiKeys": [
+ {
+ "apiKeyName": "",
+ "publicKey": "",
+ "curveType": "",
+ "expirationSeconds": ""
+ }
+ ],
+ "authenticators": [
+ {
+ "authenticatorName": "",
+ "challenge": "",
+ "attestation": {
+ "credentialId": "",
+ "clientDataJson": "",
+ "attestationObject": "",
+ "transports": [
+ ""
+ ]
+ }
+ }
+ ],
+ "oauthProviders": [
+ {
+ "providerName": "",
+ "oidcToken": "",
+ "oidcClaims": {
+ "iss": "",
+ "sub": "",
+ "aud": ""
+ }
+ }
+ ],
+ "wallet": {
+ "walletName": "",
+ "accounts": [
+ {
+ "curve": "",
+ "pathFormat": "",
+ "path": "",
+ "addressFormat": ""
+ }
+ ],
+ "mnemonicLength": ""
+ },
+ "clientSignature": {
+ "publicKey": "",
+ "scheme": "",
+ "message": "",
+ "signature": ""
+ }
+}'
+```
+
+
+
+
+
+```json 200
+{
+ "organizationId": "",
+ "wallet": {
+ "walletId": "",
+ "addresses": [
+ ""
+ ]
+ },
+ "userId": "",
+ "appProofs": [
+ {
+ "scheme": "",
+ "publicKey": "",
+ "proofPayload": "",
+ "signature": ""
+ }
+ ]
+}
+```
+
+
diff --git a/api-reference/auth-proxy/wallet-kit-config.mdx b/api-reference/auth-proxy/wallet-kit-config.mdx
new file mode 100644
index 00000000..cea380c6
--- /dev/null
+++ b/api-reference/auth-proxy/wallet-kit-config.mdx
@@ -0,0 +1,73 @@
+---
+title: "Get WalletKit Config"
+description: "Get wallet kit settings and feature toggles for the calling organization."
+---
+
+import { H3Bordered } from "/snippets/h3-bordered.mdx";
+import { NestedParam } from "/snippets/nested-param.mdx";
+
+
+
+
+
POST
+
https://authproxy.turnkey.com/v1/wallet_kit_config
+
+
+
+
+
+
+
+ Your Auth Proxy config ID, found in **Dashboard → AUTH**. See [Auth Proxy reference](/reference/auth-proxy) for setup.
+
+
+
+
+A successful response returns the following fields:
+
+
+ List of enabled authentication providers (e.g., 'facebook', 'google', 'apple', 'email', 'sms', 'passkey', 'wallet')
+
+
+item field
+
+
+
+
+Session expiration duration in seconds
+The organization ID this configuration applies to
+Mapping of social login providers to their OAuth client IDs.
+OAuth redirect URL to be used for social login flows.
+otpAlphanumeric field
+otpLength field
+
+
+
+```bash title="cURL"
+curl --request POST \
+ --url https://authproxy.turnkey.com/v1/wallet_kit_config \
+ --header 'Accept: application/json' \
+ --header 'Content-Type: application/json' \
+ --header "X-Auth-Proxy-Config-Id: (see Authorizations)" \
+ --data '{}'
+```
+
+
+
+
+
+```json 200
+{
+ "enabledProviders": [
+ ""
+ ],
+ "sessionExpirationSeconds": "",
+ "organizationId": "",
+ "oauthClientIds": "",
+ "oauthRedirectUrl": "",
+ "otpAlphanumeric": "",
+ "otpLength": ""
+}
+```
+
+
diff --git a/docs.json b/docs.json
index d927c368..888cb9b0 100644
--- a/docs.json
+++ b/docs.json
@@ -8,7 +8,12 @@
"dark": "#050a0b"
},
"contextual": {
- "options": ["copy", "view", "chatgpt", "claude"]
+ "options": [
+ "copy",
+ "view",
+ "chatgpt",
+ "claude"
+ ]
},
"integrations": {
"ga4": {
@@ -867,6 +872,19 @@
"api-reference/queries/list-webhook-endpoints",
"api-reference/queries/who-am-i"
]
+ },
+ {
+ "group": "Auth Proxy",
+ "pages": [
+ "api-reference/auth-proxy/account",
+ "api-reference/auth-proxy/oauth-login",
+ "api-reference/auth-proxy/oauth2-authenticate",
+ "api-reference/auth-proxy/otp-init",
+ "api-reference/auth-proxy/otp-login",
+ "api-reference/auth-proxy/otp-verify",
+ "api-reference/auth-proxy/signup",
+ "api-reference/auth-proxy/wallet-kit-config"
+ ]
}
]
},
diff --git a/proxy_api.swagger.json b/proxy_api.swagger.json
new file mode 100644
index 00000000..088f04f5
--- /dev/null
+++ b/proxy_api.swagger.json
@@ -0,0 +1,1159 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "title": "services/auth_proxy/v1/proxy_api.proto",
+ "version": "version not set"
+ },
+ "consumes": ["application/json"],
+ "produces": ["application/json"],
+ "paths": {
+ "/v1/account": {
+ "post": {
+ "summary": "Get Account",
+ "description": "Return organization id associated with a given phone number, email, public key, credential ID or OIDC token.",
+ "operationId": "GetAccount",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/GetAccountResponse"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/GetAccountRequest"
+ }
+ }
+ ],
+ "tags": ["Accounts"]
+ }
+ },
+ "/v1/oauth2_authenticate": {
+ "post": {
+ "summary": "OAuth 2.0 Authenticate",
+ "description": "Authenticate with an OAuth 2.0 provider and receive an OIDC token issued by Turnkey in response.",
+ "operationId": "OAuth2Authenticate",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/OAuth2AuthenticateResponse"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/OAuth2AuthenticateRequest"
+ }
+ }
+ ],
+ "tags": ["Auth"]
+ }
+ },
+ "/v1/oauth_login": {
+ "post": {
+ "summary": "OAuth Login",
+ "description": "Login using an OIDC token and public key.",
+ "operationId": "OAuthLogin",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/OAuthLoginResponse"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/OAuthLoginRequest"
+ }
+ }
+ ],
+ "tags": ["Sessions"]
+ }
+ },
+ "/v1/otp_init": {
+ "post": {
+ "summary": "Init OTP",
+ "description": "Initialize an OTP (email or SMS) for a user.",
+ "operationId": "InitOtp",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/InitOtpResponse"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/InitOtpRequest"
+ }
+ }
+ ],
+ "tags": ["Auth"]
+ }
+ },
+ "/v1/otp_init_v2": {
+ "post": {
+ "summary": "Init OTP",
+ "description": "Start a new OTP flow and return a new OTP flow ID.",
+ "operationId": "InitOtpV2",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/InitOtpV2Response"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/InitOtpV2Request"
+ }
+ }
+ ],
+ "tags": ["Auth"]
+ }
+ },
+ "/v1/otp_login": {
+ "post": {
+ "summary": "OTP Login",
+ "description": "Login using a verification token and public key.",
+ "operationId": "OtpLogin",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/OtpLoginResponse"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/OtpLoginRequest"
+ }
+ }
+ ],
+ "tags": ["Sessions"]
+ }
+ },
+ "/v1/otp_login_v2": {
+ "post": {
+ "summary": "OTP Login",
+ "description": "Login using an existing OTP Verification Token and a client-side signature. The signature's public key must match the public key contained within the OTP Verification Token.",
+ "operationId": "OtpLoginV2",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/OtpLoginV2Response"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/OtpLoginV2Request"
+ }
+ }
+ ],
+ "tags": ["Sessions"]
+ }
+ },
+ "/v1/otp_verify": {
+ "post": {
+ "summary": "Verify OTP",
+ "description": "Verify the OTP code previously sent to the user's contact and return a verification token.",
+ "operationId": "VerifyOtp",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/VerifyOtpResponse"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/VerifyOtpRequest"
+ }
+ }
+ ],
+ "tags": ["Auth"]
+ }
+ },
+ "/v1/otp_verify_v2": {
+ "post": {
+ "summary": "Verify OTP",
+ "description": "Verify the OTP code previously sent to the user's contact and return a verification token.",
+ "operationId": "VerifyOtpV2",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/VerifyOtpV2Response"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/VerifyOtpV2Request"
+ }
+ }
+ ],
+ "tags": ["Auth"]
+ }
+ },
+ "/v1/signup": {
+ "post": {
+ "summary": "Signup",
+ "description": "Onboard a new user.",
+ "operationId": "Signup",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/SignupResponse"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/SignupRequest"
+ }
+ }
+ ],
+ "tags": ["Auth"]
+ }
+ },
+ "/v1/signup_v2": {
+ "post": {
+ "summary": "Signup",
+ "description": "Onboard a new user.",
+ "operationId": "SignupV2",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/SignupV2Response"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/SignupV2Request"
+ }
+ }
+ ],
+ "tags": ["Auth"]
+ }
+ },
+ "/v1/wallet_kit_config": {
+ "post": {
+ "summary": "Get WalletKit Config",
+ "description": "Get wallet kit settings and feature toggles for the calling organization.",
+ "operationId": "GetWalletKitConfig",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/GetWalletKitConfigResponse"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/GetWalletKitConfigRequest"
+ }
+ }
+ ],
+ "tags": ["Wallet Kit"]
+ }
+ }
+ },
+ "definitions": {
+ "AddressFormat": {
+ "type": "string",
+ "enum": [
+ "ADDRESS_FORMAT_UNCOMPRESSED",
+ "ADDRESS_FORMAT_COMPRESSED",
+ "ADDRESS_FORMAT_ETHEREUM",
+ "ADDRESS_FORMAT_SOLANA",
+ "ADDRESS_FORMAT_COSMOS",
+ "ADDRESS_FORMAT_TRON",
+ "ADDRESS_FORMAT_SUI",
+ "ADDRESS_FORMAT_APTOS",
+ "ADDRESS_FORMAT_BITCOIN_MAINNET_P2PKH",
+ "ADDRESS_FORMAT_BITCOIN_MAINNET_P2SH",
+ "ADDRESS_FORMAT_BITCOIN_MAINNET_P2WPKH",
+ "ADDRESS_FORMAT_BITCOIN_MAINNET_P2WSH",
+ "ADDRESS_FORMAT_BITCOIN_MAINNET_P2TR",
+ "ADDRESS_FORMAT_BITCOIN_TESTNET_P2PKH",
+ "ADDRESS_FORMAT_BITCOIN_TESTNET_P2SH",
+ "ADDRESS_FORMAT_BITCOIN_TESTNET_P2WPKH",
+ "ADDRESS_FORMAT_BITCOIN_TESTNET_P2WSH",
+ "ADDRESS_FORMAT_BITCOIN_TESTNET_P2TR",
+ "ADDRESS_FORMAT_BITCOIN_SIGNET_P2PKH",
+ "ADDRESS_FORMAT_BITCOIN_SIGNET_P2SH",
+ "ADDRESS_FORMAT_BITCOIN_SIGNET_P2WPKH",
+ "ADDRESS_FORMAT_BITCOIN_SIGNET_P2WSH",
+ "ADDRESS_FORMAT_BITCOIN_SIGNET_P2TR",
+ "ADDRESS_FORMAT_BITCOIN_REGTEST_P2PKH",
+ "ADDRESS_FORMAT_BITCOIN_REGTEST_P2SH",
+ "ADDRESS_FORMAT_BITCOIN_REGTEST_P2WPKH",
+ "ADDRESS_FORMAT_BITCOIN_REGTEST_P2WSH",
+ "ADDRESS_FORMAT_BITCOIN_REGTEST_P2TR",
+ "ADDRESS_FORMAT_SEI",
+ "ADDRESS_FORMAT_XLM",
+ "ADDRESS_FORMAT_DOGE_MAINNET",
+ "ADDRESS_FORMAT_DOGE_TESTNET",
+ "ADDRESS_FORMAT_TON_V3R2",
+ "ADDRESS_FORMAT_TON_V4R2",
+ "ADDRESS_FORMAT_TON_V5R1",
+ "ADDRESS_FORMAT_XRP",
+ "ADDRESS_FORMAT_SPARK_MAINNET",
+ "ADDRESS_FORMAT_SPARK_REGTEST"
+ ]
+ },
+ "ApiKeyCurve": {
+ "type": "string",
+ "enum": [
+ "API_KEY_CURVE_P256",
+ "API_KEY_CURVE_SECP256K1",
+ "API_KEY_CURVE_ED25519"
+ ]
+ },
+ "ApiKeyParamsV2": {
+ "type": "object",
+ "properties": {
+ "apiKeyName": {
+ "type": "string",
+ "description": "Human-readable name for an API Key."
+ },
+ "publicKey": {
+ "type": "string",
+ "description": "The public component of a cryptographic key pair used to sign messages and transactions."
+ },
+ "curveType": {
+ "$ref": "#/definitions/ApiKeyCurve",
+ "description": "The curve type to be used for processing API key signatures."
+ },
+ "expirationSeconds": {
+ "type": "string",
+ "x-nullable": true,
+ "description": "Optional window (in seconds) indicating how long the API Key should last."
+ }
+ },
+ "required": ["apiKeyName", "publicKey", "curveType"]
+ },
+ "AppProof": {
+ "type": "object",
+ "properties": {
+ "scheme": {
+ "$ref": "#/definitions/data.v1.SignatureScheme",
+ "description": "Scheme of signing key."
+ },
+ "publicKey": {
+ "type": "string",
+ "description": "Ephemeral public key."
+ },
+ "proofPayload": {
+ "type": "string",
+ "description": "JSON serialized AppProofPayload."
+ },
+ "signature": {
+ "type": "string",
+ "description": "Signature over hashed proof_payload."
+ }
+ },
+ "required": ["scheme", "publicKey", "proofPayload", "signature"]
+ },
+ "Attestation": {
+ "type": "object",
+ "properties": {
+ "credentialId": {
+ "type": "string",
+ "description": "The cbor encoded then base64 url encoded id of the credential."
+ },
+ "clientDataJson": {
+ "type": "string",
+ "description": "A base64 url encoded payload containing metadata about the signing context and the challenge."
+ },
+ "attestationObject": {
+ "type": "string",
+ "description": "A base64 url encoded payload containing authenticator data and any attestation the webauthn provider chooses."
+ },
+ "transports": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/AuthenticatorTransport"
+ },
+ "description": "The type of authenticator transports."
+ }
+ },
+ "required": [
+ "credentialId",
+ "clientDataJson",
+ "attestationObject",
+ "transports"
+ ]
+ },
+ "AuthenticatorParamsV2": {
+ "type": "object",
+ "properties": {
+ "authenticatorName": {
+ "type": "string",
+ "description": "Human-readable name for an Authenticator."
+ },
+ "challenge": {
+ "type": "string",
+ "description": "Challenge presented for authentication purposes."
+ },
+ "attestation": {
+ "$ref": "#/definitions/Attestation",
+ "description": "The attestation that proves custody of the authenticator and provides metadata about it."
+ }
+ },
+ "required": ["authenticatorName", "challenge", "attestation"]
+ },
+ "AuthenticatorTransport": {
+ "type": "string",
+ "enum": [
+ "AUTHENTICATOR_TRANSPORT_BLE",
+ "AUTHENTICATOR_TRANSPORT_INTERNAL",
+ "AUTHENTICATOR_TRANSPORT_NFC",
+ "AUTHENTICATOR_TRANSPORT_USB",
+ "AUTHENTICATOR_TRANSPORT_HYBRID"
+ ]
+ },
+ "ClientSignature": {
+ "type": "object",
+ "properties": {
+ "publicKey": {
+ "type": "string",
+ "description": "The public component of a cryptographic key pair used to create the signature."
+ },
+ "scheme": {
+ "$ref": "#/definitions/ClientSignatureScheme",
+ "description": "The signature scheme used to generate the client signature."
+ },
+ "message": {
+ "type": "string",
+ "description": "The message that was signed."
+ },
+ "signature": {
+ "type": "string",
+ "description": "The cryptographic signature over the message."
+ }
+ },
+ "required": ["publicKey", "scheme", "message", "signature"]
+ },
+ "ClientSignatureScheme": {
+ "type": "string",
+ "enum": ["CLIENT_SIGNATURE_SCHEME_API_P256"]
+ },
+ "Curve": {
+ "type": "string",
+ "enum": ["CURVE_SECP256K1", "CURVE_ED25519", "CURVE_P256"]
+ },
+ "GetAccountRequest": {
+ "type": "object",
+ "properties": {
+ "filterType": {
+ "type": "string",
+ "description": "Specifies the type of filter to apply, i.e 'CREDENTIAL_ID', 'NAME', 'USERNAME', 'EMAIL', 'PHONE_NUMBER', 'OIDC_TOKEN' or 'PUBLIC_KEY'"
+ },
+ "filterValue": {
+ "type": "string",
+ "description": "The value of the filter to apply for the specified type. For example, a specific email or name string."
+ },
+ "verificationToken": {
+ "type": "string",
+ "x-nullable": true,
+ "description": "Signed JWT containing a unique id, expiry, verification type, contact. Used to verify access to PII (email/phone number) when filter_type is 'EMAIL' or 'PHONE_NUMBER'."
+ },
+ "oidcToken": {
+ "type": "string",
+ "x-nullable": true,
+ "description": "OIDC token to verify access to PII (email/phone number) when filter_type is 'EMAIL' or 'PHONE_NUMBER'. Needed for social linking when verification_token is not available."
+ }
+ },
+ "required": ["filterType", "filterValue"]
+ },
+ "GetAccountResponse": {
+ "type": "object",
+ "properties": {
+ "organizationId": {
+ "type": "string",
+ "x-nullable": true
+ }
+ }
+ },
+ "GetWalletKitConfigRequest": {
+ "type": "object"
+ },
+ "GetWalletKitConfigResponse": {
+ "type": "object",
+ "properties": {
+ "enabledProviders": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of enabled authentication providers (e.g., 'facebook', 'google', 'apple', 'email', 'sms', 'passkey', 'wallet')",
+ "title": "Enabled Providers"
+ },
+ "sessionExpirationSeconds": {
+ "type": "string",
+ "description": "Session expiration duration in seconds",
+ "title": "Session Expiration"
+ },
+ "organizationId": {
+ "type": "string",
+ "description": "The organization ID this configuration applies to",
+ "title": "Organization ID"
+ },
+ "oauthClientIds": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Mapping of social login providers to their OAuth client IDs.",
+ "title": "OAuth Client IDs"
+ },
+ "oauthRedirectUrl": {
+ "type": "string",
+ "description": "OAuth redirect URL to be used for social login flows.",
+ "title": "OAuth Redirect URL"
+ },
+ "otpAlphanumeric": {
+ "type": "boolean",
+ "x-nullable": true
+ },
+ "otpLength": {
+ "type": "string",
+ "x-nullable": true
+ }
+ },
+ "required": [
+ "enabledProviders",
+ "sessionExpirationSeconds",
+ "organizationId"
+ ]
+ },
+ "InitOtpRequest": {
+ "type": "object",
+ "properties": {
+ "otpType": {
+ "type": "string",
+ "description": "Enum to specify whether to send OTP via SMS or email"
+ },
+ "contact": {
+ "type": "string",
+ "description": "Email or phone number to send the OTP code to"
+ }
+ },
+ "required": ["otpType", "contact"]
+ },
+ "InitOtpResponse": {
+ "type": "object",
+ "properties": {
+ "otpId": {
+ "type": "string",
+ "description": "Unique identifier for an OTP authentication"
+ }
+ },
+ "required": ["otpId"]
+ },
+ "InitOtpV2Request": {
+ "type": "object",
+ "properties": {
+ "otpType": {
+ "type": "string",
+ "description": "Enum to specify whether to send OTP code via SMS or email"
+ },
+ "contact": {
+ "type": "string",
+ "description": "Email or phone number to send the OTP code to"
+ }
+ },
+ "required": ["otpType", "contact"]
+ },
+ "InitOtpV2Response": {
+ "type": "object",
+ "properties": {
+ "otpId": {
+ "type": "string",
+ "description": "Unique identifier for an OTP flow."
+ },
+ "otpEncryptionTargetBundle": {
+ "type": "string",
+ "description": "Signed bundle containing a target encryption key to use when submitting OTP codes."
+ }
+ },
+ "required": ["otpId", "otpEncryptionTargetBundle"]
+ },
+ "OAuth2AuthenticateRequest": {
+ "type": "object",
+ "properties": {
+ "provider": {
+ "$ref": "#/definitions/Oauth2Provider",
+ "description": "The OAuth 2.0 provider to authenticate with"
+ },
+ "authCode": {
+ "type": "string",
+ "description": "The auth_code provided by the OAuth 2.0 to the end user to be exchanged for a Bearer token in the OAuth 2.0 flow"
+ },
+ "redirectUri": {
+ "type": "string",
+ "description": "The URI the user is redirected to after they have authenticated with the OAuth 2.0 provider"
+ },
+ "codeVerifier": {
+ "type": "string",
+ "description": "The code verifier used by OAuth 2.0 PKCE providers"
+ },
+ "nonce": {
+ "type": "string",
+ "x-nullable": true,
+ "description": "An optional nonce used by the client to prevent replay/substitution of an ID token"
+ },
+ "clientId": {
+ "type": "string",
+ "description": "The client ID registered with the OAuth 2.0 provider"
+ }
+ },
+ "required": [
+ "provider",
+ "authCode",
+ "redirectUri",
+ "codeVerifier",
+ "clientId"
+ ]
+ },
+ "OAuth2AuthenticateResponse": {
+ "type": "object",
+ "properties": {
+ "oidcToken": {
+ "type": "string",
+ "description": "A Turnkey issued OIDC token to be used with the LoginWithOAuth activity"
+ }
+ },
+ "required": ["oidcToken"]
+ },
+ "OAuthLoginRequest": {
+ "type": "object",
+ "properties": {
+ "oidcToken": {
+ "type": "string",
+ "description": "Base64 encoded OIDC token"
+ },
+ "publicKey": {
+ "type": "string",
+ "description": "Client-side public key generated by the user, which will be conditionally added to org data based on the validity of the oidc token associated with this request"
+ },
+ "invalidateExisting": {
+ "type": "boolean",
+ "x-nullable": true,
+ "description": "Invalidate all other previously generated Login API keys"
+ },
+ "organizationId": {
+ "type": "string",
+ "x-nullable": true,
+ "description": "Unique identifier for a given Organization. If provided, this organization id will be used directly. If omitted, uses the OIDC token to look up the associated organization id."
+ }
+ },
+ "required": ["oidcToken", "publicKey"]
+ },
+ "OAuthLoginResponse": {
+ "type": "object",
+ "properties": {
+ "session": {
+ "type": "string",
+ "description": "Signed JWT containing an expiry, public key, session type, user id, and organization id"
+ }
+ },
+ "required": ["session"]
+ },
+ "Oauth2Provider": {
+ "type": "string",
+ "enum": ["OAUTH2_PROVIDER_X", "OAUTH2_PROVIDER_DISCORD"]
+ },
+ "OauthProviderParams": {
+ "type": "object",
+ "properties": {
+ "providerName": {
+ "type": "string",
+ "description": "Human-readable name to identify a Provider."
+ },
+ "oidcToken": {
+ "type": "string",
+ "description": "Base64 encoded OIDC token"
+ }
+ },
+ "required": ["providerName", "oidcToken"]
+ },
+ "OauthProviderParamsV2": {
+ "type": "object",
+ "properties": {
+ "providerName": {
+ "type": "string",
+ "description": "Human-readable name to identify a Provider."
+ },
+ "oidcToken": {
+ "type": "string",
+ "description": "Base64 encoded OIDC token"
+ },
+ "oidcClaims": {
+ "$ref": "#/definitions/OidcClaims",
+ "description": "OIDC claims (iss, sub, aud) to uniquely identify the user"
+ }
+ },
+ "required": ["providerName"]
+ },
+ "OidcClaims": {
+ "type": "object",
+ "properties": {
+ "iss": {
+ "type": "string",
+ "description": "The issuer identifier from the OIDC token (iss claim)"
+ },
+ "sub": {
+ "type": "string",
+ "description": "The subject identifier from the OIDC token (sub claim)"
+ },
+ "aud": {
+ "type": "string",
+ "description": "The audience from the OIDC token (aud claim)"
+ }
+ },
+ "required": ["iss", "sub", "aud"]
+ },
+ "OtpLoginRequest": {
+ "type": "object",
+ "properties": {
+ "verificationToken": {
+ "type": "string",
+ "description": "Signed JWT containing a unique id, expiry, verification type, contact. Verification status of a user is updated when the token is consumed (in OTP_LOGIN requests)"
+ },
+ "publicKey": {
+ "type": "string",
+ "description": "Client-side public key generated by the user, which will be conditionally added to org data based on the validity of the verification token"
+ },
+ "invalidateExisting": {
+ "type": "boolean",
+ "x-nullable": true,
+ "description": "Invalidate all other previously generated Login API keys"
+ },
+ "organizationId": {
+ "type": "string",
+ "x-nullable": true,
+ "description": "Unique identifier for a given Organization. If provided, this organization id will be used directly. If omitted, uses the verification token to look up the verified sub-organization based on the contact and verification type."
+ },
+ "clientSignature": {
+ "$ref": "#/definitions/ClientSignature",
+ "x-nullable": true,
+ "description": "Optional signature proving authorization for this login. The signature is over the verification token ID and the public key. Only required if a public key was provided during the verification step."
+ }
+ },
+ "required": ["verificationToken", "publicKey"]
+ },
+ "OtpLoginResponse": {
+ "type": "object",
+ "properties": {
+ "session": {
+ "type": "string",
+ "description": "Signed JWT containing an expiry, public key, session type, user id, and organization id"
+ }
+ },
+ "required": ["session"]
+ },
+ "OtpLoginV2Request": {
+ "type": "object",
+ "properties": {
+ "verificationToken": {
+ "type": "string",
+ "description": "Session containing a unique id, expiry, verification type, contact. Verification status of a user is updated when the token is consumed (in OTP_LOGIN requests)"
+ },
+ "publicKey": {
+ "type": "string",
+ "description": "Client-side public key generated by the user, used as the session public key upon successful login."
+ },
+ "clientSignature": {
+ "$ref": "#/definitions/ClientSignature",
+ "description": "Signature proving authorization for this login. The signature is over the verification token ID and the new session public key."
+ },
+ "invalidateExisting": {
+ "type": "boolean",
+ "x-nullable": true,
+ "description": "Invalidate all other previously generated Login sessions"
+ },
+ "organizationId": {
+ "type": "string",
+ "x-nullable": true,
+ "description": "Unique identifier for a given Organization. If provided, this organization id will be used directly. If omitted, uses the verification token to look up the verified sub-organization based on the contact and verification type."
+ }
+ },
+ "required": ["verificationToken", "publicKey", "clientSignature"]
+ },
+ "OtpLoginV2Response": {
+ "type": "object",
+ "properties": {
+ "session": {
+ "type": "string",
+ "description": "Session containing an expiry, public key, session type, user id, and organization id"
+ }
+ },
+ "required": ["session"]
+ },
+ "PathFormat": {
+ "type": "string",
+ "enum": ["PATH_FORMAT_BIP32"]
+ },
+ "SignupRequest": {
+ "type": "object",
+ "properties": {
+ "userEmail": {
+ "type": "string",
+ "x-nullable": true
+ },
+ "userPhoneNumber": {
+ "type": "string",
+ "x-nullable": true
+ },
+ "userTag": {
+ "type": "string",
+ "x-nullable": true
+ },
+ "userName": {
+ "type": "string"
+ },
+ "organizationName": {
+ "type": "string"
+ },
+ "verificationToken": {
+ "type": "string",
+ "x-nullable": true
+ },
+ "apiKeys": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/ApiKeyParamsV2"
+ },
+ "description": "A list of API Key parameters. This field, if not needed, should be an empty array in your request body."
+ },
+ "authenticators": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/AuthenticatorParamsV2"
+ },
+ "description": "A list of Authenticator parameters. This field, if not needed, should be an empty array in your request body."
+ },
+ "oauthProviders": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/OauthProviderParams"
+ },
+ "description": "A list of Oauth providers. This field, if not needed, should be an empty array in your request body."
+ },
+ "wallet": {
+ "$ref": "#/definitions/WalletParams",
+ "x-nullable": true,
+ "description": "The wallet to create for the sub-organization"
+ },
+ "clientSignature": {
+ "$ref": "#/definitions/ClientSignature",
+ "x-nullable": true,
+ "description": "Optional signature proving authorization for this signup. The signature is over the verification token ID and the root user parameters for the root user associated with the verification token. Only required if a public key was provided during the verification step."
+ }
+ },
+ "required": ["apiKeys", "authenticators", "oauthProviders"]
+ },
+ "SignupResponse": {
+ "type": "object",
+ "properties": {
+ "organizationId": {
+ "type": "string"
+ },
+ "wallet": {
+ "$ref": "#/definitions/WalletResult",
+ "description": "Wallet created for the sub-organization, if provided in the request",
+ "title": "Wallet"
+ },
+ "userId": {
+ "type": "string",
+ "description": "Root user ID created for this sub-organization",
+ "title": "User ID"
+ },
+ "appProofs": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/AppProof"
+ },
+ "description": "A list of App Proofs generated by enclaves during activity execution, providing verifiable attestations of performed operations."
+ }
+ },
+ "required": ["organizationId", "userId"]
+ },
+ "SignupV2Request": {
+ "type": "object",
+ "properties": {
+ "userEmail": {
+ "type": "string",
+ "x-nullable": true
+ },
+ "userPhoneNumber": {
+ "type": "string",
+ "x-nullable": true
+ },
+ "userTag": {
+ "type": "string",
+ "x-nullable": true
+ },
+ "userName": {
+ "type": "string"
+ },
+ "organizationName": {
+ "type": "string"
+ },
+ "verificationToken": {
+ "type": "string",
+ "x-nullable": true
+ },
+ "apiKeys": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/ApiKeyParamsV2"
+ },
+ "description": "A list of API Key parameters. This field, if not needed, should be an empty array in your request body."
+ },
+ "authenticators": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/AuthenticatorParamsV2"
+ },
+ "description": "A list of Authenticator parameters. This field, if not needed, should be an empty array in your request body."
+ },
+ "oauthProviders": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/OauthProviderParamsV2"
+ },
+ "description": "A list of Oauth providers. This field, if not needed, should be an empty array in your request body."
+ },
+ "wallet": {
+ "$ref": "#/definitions/WalletParams",
+ "x-nullable": true,
+ "description": "The wallet to create for the sub-organization"
+ },
+ "clientSignature": {
+ "$ref": "#/definitions/ClientSignature",
+ "x-nullable": true,
+ "description": "Optional signature proving authorization for this signup. The signature is over the verification token ID and the root user parameters for the root user associated with the verification token. Only required if a public key was provided during the verification step."
+ }
+ },
+ "required": ["apiKeys", "authenticators", "oauthProviders"]
+ },
+ "SignupV2Response": {
+ "type": "object",
+ "properties": {
+ "organizationId": {
+ "type": "string"
+ },
+ "wallet": {
+ "$ref": "#/definitions/WalletResult",
+ "description": "Wallet created for the sub-organization, if provided in the request",
+ "title": "Wallet"
+ },
+ "userId": {
+ "type": "string",
+ "description": "Root user ID created for this sub-organization",
+ "title": "User ID"
+ },
+ "appProofs": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/AppProof"
+ },
+ "description": "A list of App Proofs generated by enclaves during activity execution, providing verifiable attestations of performed operations."
+ }
+ },
+ "required": ["organizationId", "userId"]
+ },
+ "VerifyOtpRequest": {
+ "type": "object",
+ "properties": {
+ "otpId": {
+ "type": "string",
+ "description": "ID representing the result of an init OTP activity."
+ },
+ "otpCode": {
+ "type": "string",
+ "description": "OTP sent out to a user's contact (email or SMS)"
+ },
+ "publicKey": {
+ "type": "string",
+ "x-nullable": true,
+ "description": "Client-side public key generated by the user, which will be added to the JWT response and verified in subsequent requests via a client proof signature"
+ }
+ },
+ "required": ["otpId", "otpCode"]
+ },
+ "VerifyOtpResponse": {
+ "type": "object",
+ "properties": {
+ "verificationToken": {
+ "type": "string",
+ "description": "Signed JWT containing a unique id, expiry, verification type, contact. Verification status of a user is updated when the token is consumed (in OTP_LOGIN requests)"
+ }
+ },
+ "required": ["verificationToken"]
+ },
+ "VerifyOtpV2Request": {
+ "type": "object",
+ "properties": {
+ "otpId": {
+ "type": "string",
+ "description": "ID representing the result of an init OTP activity."
+ },
+ "encryptedOtpBundle": {
+ "type": "string",
+ "description": "Encrypted bundle containing the OTP code and a client-generated public key. Turnkey's secure enclaves will decrypt this bundle, verify the OTP code, and issue a new Verification Token. Encrypted using the target encryption key provided in the INIT_OTP activity result."
+ }
+ },
+ "required": ["otpId", "encryptedOtpBundle"]
+ },
+ "VerifyOtpV2Response": {
+ "type": "object",
+ "properties": {
+ "verificationToken": {
+ "type": "string",
+ "description": "Verification Token containing a unique id, expiry, verification type, contact signed by Turnkey's enclaves. Verification status of a user is updated when the token is consumed (in OTP_LOGIN requests)"
+ }
+ },
+ "required": ["verificationToken"]
+ },
+ "WalletAccountParams": {
+ "type": "object",
+ "properties": {
+ "curve": {
+ "$ref": "#/definitions/Curve",
+ "description": "Cryptographic curve used to generate a wallet Account."
+ },
+ "pathFormat": {
+ "$ref": "#/definitions/PathFormat",
+ "description": "Path format used to generate a wallet Account."
+ },
+ "path": {
+ "type": "string",
+ "description": "Path used to generate a wallet Account."
+ },
+ "addressFormat": {
+ "$ref": "#/definitions/AddressFormat",
+ "description": "Address format used to generate a wallet Acccount."
+ }
+ },
+ "required": ["curve", "pathFormat", "path", "addressFormat"]
+ },
+ "WalletParams": {
+ "type": "object",
+ "properties": {
+ "walletName": {
+ "type": "string",
+ "description": "Human-readable name for a Wallet."
+ },
+ "accounts": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/WalletAccountParams"
+ },
+ "description": "A list of wallet Accounts. This field, if not needed, should be an empty array in your request body."
+ },
+ "mnemonicLength": {
+ "type": "integer",
+ "format": "int32",
+ "x-nullable": true,
+ "description": "Length of mnemonic to generate the Wallet seed. Defaults to 12. Accepted values: 12, 15, 18, 21, 24."
+ }
+ },
+ "required": ["walletName", "accounts"]
+ },
+ "WalletResult": {
+ "type": "object",
+ "properties": {
+ "walletId": {
+ "type": "string"
+ },
+ "addresses": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "A list of account addresses."
+ }
+ },
+ "required": ["walletId", "addresses"]
+ },
+ "data.v1.SignatureScheme": {
+ "type": "string",
+ "enum": ["SIGNATURE_SCHEME_EPHEMERAL_KEY_P256"]
+ }
+ }
+}
diff --git a/public_api.swagger.json b/public_api.swagger.json
index 09c39d03..f83e842a 100644
--- a/public_api.swagger.json
+++ b/public_api.swagger.json
@@ -14487,4 +14487,4 @@
"tags": ["Activities", "Consensus"]
}
]
-}
+}
\ No newline at end of file
diff --git a/scripts/openapi-gen/README.md b/scripts/openapi-gen/README.md
index 12b3702b..e6a94902 100644
--- a/scripts/openapi-gen/README.md
+++ b/scripts/openapi-gen/README.md
@@ -22,13 +22,24 @@ A utility for parsing, dereferencing, and generating content from OpenAPI specif
npx ts-node openapi-gen.ts --file openapi.json --endpoints --generate-mdx --mdx-output-dir api-reference
```
+## Auth Proxy API Reference
+
+The auth proxy is generated as part of `make gen` — no separate command needed. Both swagger files must be present first (see the top-level README for setup).
+
+This converts `proxy_api.swagger.json` → `scripts/openapi-gen/proxy_api_openapi.json`, then generates MDX files under `api-reference/auth-proxy/` and updates the `Auth Proxy` group in `docs.json`.
+
+Key differences from the main API generation:
+- `--auth-proxy`: uses the auth-proxy generator (flat output, hardcoded base URL and auth header, v1 endpoint deduplication)
+- `--nav-group`: targets the `Auth Proxy` group in `docs.json` instead of Activities/Queries
+
## File Structure
```text
scripts/openapi-gen/
├── openapi-gen.ts # CLI entrypoint and core logic
-├── swagger-to-openapi.ts # Script to convert the public_api.swagger.json to openapi v3
-├── openapi.json # The openapi v3 result of the conversion script
+├── swagger-to-openapi.ts # Script to convert swagger to openapi v3 (supports --service=auth-proxy)
+├── openapi.json # OpenAPI v3 output for the main Turnkey API
+├── proxy_api_openapi.json # OpenAPI v3 output for the Auth Proxy API (generated, not committed)
├── utils/
│ ├── cli.ts # Commander-based argument parsing
│ ├── parser.ts # OpenAPI spec parsing & dereferencing
diff --git a/scripts/openapi-gen/openapi-gen.ts b/scripts/openapi-gen/openapi-gen.ts
index 390ccf91..7b0bc1df 100644
--- a/scripts/openapi-gen/openapi-gen.ts
+++ b/scripts/openapi-gen/openapi-gen.ts
@@ -16,7 +16,7 @@ import {
formatApiEndpoints,
ApiEndpointParserResult,
} from "./utils/endpoint-parser";
-import { generateMdxFile } from "./utils/mdx-generator";
+import { generateMdxFile, generateAuthProxyMdxFile } from "./utils/mdx-generator";
import path from "path";
import fs from "fs";
@@ -139,23 +139,40 @@ export const tags = ${tagsStr};`;
fs.mkdirSync(absoluteMdxOutputDir, { recursive: true });
console.log(`Endpoint count: ${endpointResult.endpoints.length}`);
+ // In auth-proxy mode, build a set of endpoint paths to skip — those for which
+ // a higher-versioned sibling exists (e.g. skip /v1/otp_init when /v1/otp_init_v2 is present).
+ const skipEndpointPaths = new Set();
+ if (options.authProxy) {
+ const versionSuffix = /_v(\d+)$/;
+ const allPaths = endpointResult.endpoints.map((e) => e.path);
+ for (const p of allPaths) {
+ const base = p.replace(versionSuffix, "");
+ if (base !== p) {
+ // This is a versioned path — mark the unversioned base (v1) for skipping
+ skipEndpointPaths.add(base);
+ }
+ }
+ }
+
// Collect generated MDX paths for docs.json update
const activityPaths: string[] = [];
const queryPaths: string[] = [];
+ const authProxyPaths: string[] = [];
for (const endpoint of endpointResult.endpoints) {
- // Pass the mdxAddOnly flag and capture the generated path
- const generatedPath = generateMdxFile(
- endpoint,
- absoluteMdxOutputDir,
- options.mdxAddOnly
- );
+ if (skipEndpointPaths.has(endpoint.path)) continue;
+ // Use the auth-proxy generator when --auth-proxy is set, main generator otherwise
+ const generatedPath = options.authProxy
+ ? generateAuthProxyMdxFile(endpoint, absoluteMdxOutputDir, options.mdxAddOnly)
+ : generateMdxFile(endpoint, absoluteMdxOutputDir, options.mdxAddOnly);
if (generatedPath) {
// Construct the full path needed for docs.json
- const fullDocsPath = path.join("api-reference", generatedPath);
+ const fullDocsPath = path.join(relativeMdxBaseDir, generatedPath);
- if (endpoint.type === "activity") {
+ if (options.authProxy) {
+ authProxyPaths.push(fullDocsPath);
+ } else if (endpoint.type === "activity") {
activityPaths.push(fullDocsPath);
} else if (endpoint.type === "query") {
queryPaths.push(fullDocsPath);
@@ -187,10 +204,12 @@ export const tags = ${tagsStr};`;
// Remove duplicates before sorting
const uniqueActivityPaths = [...new Set(activityPaths)];
const uniqueQueryPaths = [...new Set(queryPaths)];
+ const uniqueAuthProxyPaths = [...new Set(authProxyPaths)];
// Sort generated paths alphabetically
uniqueActivityPaths.sort();
uniqueQueryPaths.sort();
+ uniqueAuthProxyPaths.sort();
// --- Find and Update Navigation ---
// Check if docsConfig.navigation is an array before proceeding
@@ -198,7 +217,7 @@ export const tags = ${tagsStr};`;
console.error(
`Error: Expected 'docs.json' to have a top-level 'navigation' array.`
);
- throw new Error("'docs.json' structure is not as expected."); // Or handle more gracefully
+ throw new Error("'docs.json' structure is not as expected.");
}
// Find the API Reference tab
@@ -207,38 +226,51 @@ export const tags = ${tagsStr};`;
);
if (apiRefTab && Array.isArray(apiRefTab.pages)) {
- // Find and update Activities group
- const activitiesGroup = apiRefTab.pages.find(
- (item: any) =>
- typeof item === "object" && item.group === "Activities"
- );
- if (activitiesGroup) {
- activitiesGroup.pages = [
- activityOverviewPath,
- ...uniqueActivityPaths,
- ];
- console.log(`Updated Activities paths in docs.json`);
- } else {
- console.warn(
- `Could not find 'Activities' group in docs.json under 'API Reference'`
+ if (options.authProxy && options.navGroup) {
+ // Auth-proxy mode: find or create the named nav group and set its pages
+ let navGroup = apiRefTab.pages.find(
+ (item: any) => typeof item === "object" && item.group === options.navGroup
);
- }
-
- // Find and update Queries group
- const queriesGroup = apiRefTab.pages.find(
- (item: any) => typeof item === "object" && item.group === "Queries"
- );
- if (queriesGroup) {
- queriesGroup.pages = [queryOverviewPath, ...uniqueQueryPaths];
- console.log(`Updated Queries paths in docs.json`);
+ if (!navGroup) {
+ navGroup = { group: options.navGroup, pages: [] };
+ apiRefTab.pages.push(navGroup);
+ console.log(`Created new nav group '${options.navGroup}' in docs.json`);
+ }
+ navGroup.pages = uniqueAuthProxyPaths;
+ console.log(`Updated '${options.navGroup}' paths in docs.json`);
} else {
- console.warn(
- `Could not find 'Queries' group in docs.json under 'API Reference - V2'`
+ // Standard mode: update Activities and Queries groups
+ const activitiesGroup = apiRefTab.pages.find(
+ (item: any) =>
+ typeof item === "object" && item.group === "Activities"
+ );
+ if (activitiesGroup) {
+ activitiesGroup.pages = [
+ activityOverviewPath,
+ ...uniqueActivityPaths,
+ ];
+ console.log(`Updated Activities paths in docs.json`);
+ } else {
+ console.warn(
+ `Could not find 'Activities' group in docs.json under 'API Reference'`
+ );
+ }
+
+ const queriesGroup = apiRefTab.pages.find(
+ (item: any) => typeof item === "object" && item.group === "Queries"
);
+ if (queriesGroup) {
+ queriesGroup.pages = [queryOverviewPath, ...uniqueQueryPaths];
+ console.log(`Updated Queries paths in docs.json`);
+ } else {
+ console.warn(
+ `Could not find 'Queries' group in docs.json under 'API Reference'`
+ );
+ }
}
} else {
console.warn(
- `Could not find 'API Reference - V2' tab in docs.json navigation`
+ `Could not find 'API reference' tab in docs.json navigation`
);
}
diff --git a/scripts/openapi-gen/proxy_api_openapi.json b/scripts/openapi-gen/proxy_api_openapi.json
new file mode 100644
index 00000000..78460550
--- /dev/null
+++ b/scripts/openapi-gen/proxy_api_openapi.json
@@ -0,0 +1,1311 @@
+{
+ "openapi": "3.0.1",
+ "info": {
+ "title": "services/auth_proxy/v1/proxy_api.proto",
+ "version": "version not set"
+ },
+ "servers": [
+ {
+ "url": "https://authproxy.turnkey.com"
+ }
+ ],
+ "paths": {
+ "/v1/account": {
+ "post": {
+ "tags": [
+ "Accounts"
+ ],
+ "summary": "Get Account",
+ "description": "Return organization id associated with a given phone number, email, public key, credential ID or OIDC token.",
+ "operationId": "GetAccount",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/GetAccountRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/GetAccountResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/oauth2_authenticate": {
+ "post": {
+ "tags": [
+ "Auth"
+ ],
+ "summary": "OAuth 2.0 Authenticate",
+ "description": "Authenticate with an OAuth 2.0 provider and receive an OIDC token issued by Turnkey in response.",
+ "operationId": "OAuth2Authenticate",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OAuth2AuthenticateRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OAuth2AuthenticateResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/oauth_login": {
+ "post": {
+ "tags": [
+ "Sessions"
+ ],
+ "summary": "OAuth Login",
+ "description": "Login using an OIDC token and public key.",
+ "operationId": "OAuthLogin",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OAuthLoginRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OAuthLoginResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/otp_init": {
+ "post": {
+ "tags": [
+ "Auth"
+ ],
+ "summary": "Init OTP",
+ "description": "Initialize an OTP (email or SMS) for a user.",
+ "operationId": "InitOtp",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InitOtpRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InitOtpResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/otp_init_v2": {
+ "post": {
+ "tags": [
+ "Auth"
+ ],
+ "summary": "Init OTP",
+ "description": "Start a new OTP flow and return a new OTP flow ID.",
+ "operationId": "InitOtpV2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InitOtpV2Request"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InitOtpV2Response"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/otp_login": {
+ "post": {
+ "tags": [
+ "Sessions"
+ ],
+ "summary": "OTP Login",
+ "description": "Login using a verification token and public key.",
+ "operationId": "OtpLogin",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OtpLoginRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OtpLoginResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/otp_login_v2": {
+ "post": {
+ "tags": [
+ "Sessions"
+ ],
+ "summary": "OTP Login",
+ "description": "Login using an existing OTP Verification Token and a client-side signature. The signature's public key must match the public key contained within the OTP Verification Token.",
+ "operationId": "OtpLoginV2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OtpLoginV2Request"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OtpLoginV2Response"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/otp_verify": {
+ "post": {
+ "tags": [
+ "Auth"
+ ],
+ "summary": "Verify OTP",
+ "description": "Verify the OTP code previously sent to the user's contact and return a verification token.",
+ "operationId": "VerifyOtp",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VerifyOtpRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VerifyOtpResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/otp_verify_v2": {
+ "post": {
+ "tags": [
+ "Auth"
+ ],
+ "summary": "Verify OTP",
+ "description": "Verify the OTP code previously sent to the user's contact and return a verification token.",
+ "operationId": "VerifyOtpV2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VerifyOtpV2Request"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VerifyOtpV2Response"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/signup": {
+ "post": {
+ "tags": [
+ "Auth"
+ ],
+ "summary": "Signup",
+ "description": "Onboard a new user.",
+ "operationId": "Signup",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SignupRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SignupResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/signup_v2": {
+ "post": {
+ "tags": [
+ "Auth"
+ ],
+ "summary": "Signup",
+ "description": "Onboard a new user.",
+ "operationId": "SignupV2",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SignupV2Request"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SignupV2Response"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/wallet_kit_config": {
+ "post": {
+ "tags": [
+ "Wallet Kit"
+ ],
+ "summary": "Get WalletKit Config",
+ "description": "Get wallet kit settings and feature toggles for the calling organization.",
+ "operationId": "GetWalletKitConfig",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/GetWalletKitConfigRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/GetWalletKitConfigResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "AddressFormat": {
+ "type": "string",
+ "enum": [
+ "ADDRESS_FORMAT_UNCOMPRESSED",
+ "ADDRESS_FORMAT_COMPRESSED",
+ "ADDRESS_FORMAT_ETHEREUM",
+ "ADDRESS_FORMAT_SOLANA",
+ "ADDRESS_FORMAT_COSMOS",
+ "ADDRESS_FORMAT_TRON",
+ "ADDRESS_FORMAT_SUI",
+ "ADDRESS_FORMAT_APTOS",
+ "ADDRESS_FORMAT_BITCOIN_MAINNET_P2PKH",
+ "ADDRESS_FORMAT_BITCOIN_MAINNET_P2SH",
+ "ADDRESS_FORMAT_BITCOIN_MAINNET_P2WPKH",
+ "ADDRESS_FORMAT_BITCOIN_MAINNET_P2WSH",
+ "ADDRESS_FORMAT_BITCOIN_MAINNET_P2TR",
+ "ADDRESS_FORMAT_BITCOIN_TESTNET_P2PKH",
+ "ADDRESS_FORMAT_BITCOIN_TESTNET_P2SH",
+ "ADDRESS_FORMAT_BITCOIN_TESTNET_P2WPKH",
+ "ADDRESS_FORMAT_BITCOIN_TESTNET_P2WSH",
+ "ADDRESS_FORMAT_BITCOIN_TESTNET_P2TR",
+ "ADDRESS_FORMAT_BITCOIN_SIGNET_P2PKH",
+ "ADDRESS_FORMAT_BITCOIN_SIGNET_P2SH",
+ "ADDRESS_FORMAT_BITCOIN_SIGNET_P2WPKH",
+ "ADDRESS_FORMAT_BITCOIN_SIGNET_P2WSH",
+ "ADDRESS_FORMAT_BITCOIN_SIGNET_P2TR",
+ "ADDRESS_FORMAT_BITCOIN_REGTEST_P2PKH",
+ "ADDRESS_FORMAT_BITCOIN_REGTEST_P2SH",
+ "ADDRESS_FORMAT_BITCOIN_REGTEST_P2WPKH",
+ "ADDRESS_FORMAT_BITCOIN_REGTEST_P2WSH",
+ "ADDRESS_FORMAT_BITCOIN_REGTEST_P2TR",
+ "ADDRESS_FORMAT_SEI",
+ "ADDRESS_FORMAT_XLM",
+ "ADDRESS_FORMAT_DOGE_MAINNET",
+ "ADDRESS_FORMAT_DOGE_TESTNET",
+ "ADDRESS_FORMAT_TON_V3R2",
+ "ADDRESS_FORMAT_TON_V4R2",
+ "ADDRESS_FORMAT_TON_V5R1",
+ "ADDRESS_FORMAT_XRP",
+ "ADDRESS_FORMAT_SPARK_MAINNET",
+ "ADDRESS_FORMAT_SPARK_REGTEST"
+ ]
+ },
+ "ApiKeyCurve": {
+ "type": "string",
+ "enum": [
+ "API_KEY_CURVE_P256",
+ "API_KEY_CURVE_SECP256K1",
+ "API_KEY_CURVE_ED25519"
+ ]
+ },
+ "ApiKeyParamsV2": {
+ "type": "object",
+ "properties": {
+ "apiKeyName": {
+ "type": "string",
+ "description": "Human-readable name for an API Key."
+ },
+ "publicKey": {
+ "type": "string",
+ "description": "The public component of a cryptographic key pair used to sign messages and transactions."
+ },
+ "curveType": {
+ "$ref": "#/components/schemas/ApiKeyCurve"
+ },
+ "expirationSeconds": {
+ "type": "string",
+ "description": "Optional window (in seconds) indicating how long the API Key should last.",
+ "nullable": true
+ }
+ },
+ "required": [
+ "apiKeyName",
+ "publicKey",
+ "curveType"
+ ]
+ },
+ "AppProof": {
+ "type": "object",
+ "properties": {
+ "scheme": {
+ "$ref": "#/components/schemas/data.v1.SignatureScheme"
+ },
+ "publicKey": {
+ "type": "string",
+ "description": "Ephemeral public key."
+ },
+ "proofPayload": {
+ "type": "string",
+ "description": "JSON serialized AppProofPayload."
+ },
+ "signature": {
+ "type": "string",
+ "description": "Signature over hashed proof_payload."
+ }
+ },
+ "required": [
+ "scheme",
+ "publicKey",
+ "proofPayload",
+ "signature"
+ ]
+ },
+ "Attestation": {
+ "type": "object",
+ "properties": {
+ "credentialId": {
+ "type": "string",
+ "description": "The cbor encoded then base64 url encoded id of the credential."
+ },
+ "clientDataJson": {
+ "type": "string",
+ "description": "A base64 url encoded payload containing metadata about the signing context and the challenge."
+ },
+ "attestationObject": {
+ "type": "string",
+ "description": "A base64 url encoded payload containing authenticator data and any attestation the webauthn provider chooses."
+ },
+ "transports": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AuthenticatorTransport"
+ },
+ "description": "The type of authenticator transports."
+ }
+ },
+ "required": [
+ "credentialId",
+ "clientDataJson",
+ "attestationObject",
+ "transports"
+ ]
+ },
+ "AuthenticatorParamsV2": {
+ "type": "object",
+ "properties": {
+ "authenticatorName": {
+ "type": "string",
+ "description": "Human-readable name for an Authenticator."
+ },
+ "challenge": {
+ "type": "string",
+ "description": "Challenge presented for authentication purposes."
+ },
+ "attestation": {
+ "$ref": "#/components/schemas/Attestation"
+ }
+ },
+ "required": [
+ "authenticatorName",
+ "challenge",
+ "attestation"
+ ]
+ },
+ "AuthenticatorTransport": {
+ "type": "string",
+ "enum": [
+ "AUTHENTICATOR_TRANSPORT_BLE",
+ "AUTHENTICATOR_TRANSPORT_INTERNAL",
+ "AUTHENTICATOR_TRANSPORT_NFC",
+ "AUTHENTICATOR_TRANSPORT_USB",
+ "AUTHENTICATOR_TRANSPORT_HYBRID"
+ ]
+ },
+ "ClientSignature": {
+ "type": "object",
+ "properties": {
+ "publicKey": {
+ "type": "string",
+ "description": "The public component of a cryptographic key pair used to create the signature."
+ },
+ "scheme": {
+ "$ref": "#/components/schemas/ClientSignatureScheme"
+ },
+ "message": {
+ "type": "string",
+ "description": "The message that was signed."
+ },
+ "signature": {
+ "type": "string",
+ "description": "The cryptographic signature over the message."
+ }
+ },
+ "required": [
+ "publicKey",
+ "scheme",
+ "message",
+ "signature"
+ ]
+ },
+ "ClientSignatureScheme": {
+ "type": "string",
+ "enum": [
+ "CLIENT_SIGNATURE_SCHEME_API_P256"
+ ]
+ },
+ "Curve": {
+ "type": "string",
+ "enum": [
+ "CURVE_SECP256K1",
+ "CURVE_ED25519",
+ "CURVE_P256"
+ ]
+ },
+ "GetAccountRequest": {
+ "type": "object",
+ "properties": {
+ "filterType": {
+ "type": "string",
+ "description": "Specifies the type of filter to apply, i.e 'CREDENTIAL_ID', 'NAME', 'USERNAME', 'EMAIL', 'PHONE_NUMBER', 'OIDC_TOKEN' or 'PUBLIC_KEY'"
+ },
+ "filterValue": {
+ "type": "string",
+ "description": "The value of the filter to apply for the specified type. For example, a specific email or name string."
+ },
+ "verificationToken": {
+ "type": "string",
+ "description": "Signed JWT containing a unique id, expiry, verification type, contact. Used to verify access to PII (email/phone number) when filter_type is 'EMAIL' or 'PHONE_NUMBER'.",
+ "nullable": true
+ },
+ "oidcToken": {
+ "type": "string",
+ "description": "OIDC token to verify access to PII (email/phone number) when filter_type is 'EMAIL' or 'PHONE_NUMBER'. Needed for social linking when verification_token is not available.",
+ "nullable": true
+ }
+ },
+ "required": [
+ "filterType",
+ "filterValue"
+ ]
+ },
+ "GetAccountResponse": {
+ "type": "object",
+ "properties": {
+ "organizationId": {
+ "type": "string",
+ "nullable": true
+ }
+ }
+ },
+ "GetWalletKitConfigRequest": {
+ "type": "object"
+ },
+ "GetWalletKitConfigResponse": {
+ "type": "object",
+ "properties": {
+ "enabledProviders": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "List of enabled authentication providers (e.g., 'facebook', 'google', 'apple', 'email', 'sms', 'passkey', 'wallet')",
+ "title": "Enabled Providers"
+ },
+ "sessionExpirationSeconds": {
+ "type": "string",
+ "description": "Session expiration duration in seconds",
+ "title": "Session Expiration"
+ },
+ "organizationId": {
+ "type": "string",
+ "description": "The organization ID this configuration applies to",
+ "title": "Organization ID"
+ },
+ "oauthClientIds": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Mapping of social login providers to their OAuth client IDs.",
+ "title": "OAuth Client IDs"
+ },
+ "oauthRedirectUrl": {
+ "type": "string",
+ "description": "OAuth redirect URL to be used for social login flows.",
+ "title": "OAuth Redirect URL"
+ },
+ "otpAlphanumeric": {
+ "type": "boolean",
+ "nullable": true
+ },
+ "otpLength": {
+ "type": "string",
+ "nullable": true
+ }
+ },
+ "required": [
+ "enabledProviders",
+ "sessionExpirationSeconds",
+ "organizationId"
+ ]
+ },
+ "InitOtpRequest": {
+ "type": "object",
+ "properties": {
+ "otpType": {
+ "type": "string",
+ "description": "Enum to specify whether to send OTP via SMS or email"
+ },
+ "contact": {
+ "type": "string",
+ "description": "Email or phone number to send the OTP code to"
+ }
+ },
+ "required": [
+ "otpType",
+ "contact"
+ ]
+ },
+ "InitOtpResponse": {
+ "type": "object",
+ "properties": {
+ "otpId": {
+ "type": "string",
+ "description": "Unique identifier for an OTP authentication"
+ }
+ },
+ "required": [
+ "otpId"
+ ]
+ },
+ "InitOtpV2Request": {
+ "type": "object",
+ "properties": {
+ "otpType": {
+ "type": "string",
+ "description": "Enum to specify whether to send OTP code via SMS or email"
+ },
+ "contact": {
+ "type": "string",
+ "description": "Email or phone number to send the OTP code to"
+ }
+ },
+ "required": [
+ "otpType",
+ "contact"
+ ]
+ },
+ "InitOtpV2Response": {
+ "type": "object",
+ "properties": {
+ "otpId": {
+ "type": "string",
+ "description": "Unique identifier for an OTP flow."
+ },
+ "otpEncryptionTargetBundle": {
+ "type": "string",
+ "description": "Signed bundle containing a target encryption key to use when submitting OTP codes."
+ }
+ },
+ "required": [
+ "otpId",
+ "otpEncryptionTargetBundle"
+ ]
+ },
+ "OAuth2AuthenticateRequest": {
+ "type": "object",
+ "properties": {
+ "provider": {
+ "$ref": "#/components/schemas/Oauth2Provider"
+ },
+ "authCode": {
+ "type": "string",
+ "description": "The auth_code provided by the OAuth 2.0 to the end user to be exchanged for a Bearer token in the OAuth 2.0 flow"
+ },
+ "redirectUri": {
+ "type": "string",
+ "description": "The URI the user is redirected to after they have authenticated with the OAuth 2.0 provider"
+ },
+ "codeVerifier": {
+ "type": "string",
+ "description": "The code verifier used by OAuth 2.0 PKCE providers"
+ },
+ "nonce": {
+ "type": "string",
+ "description": "An optional nonce used by the client to prevent replay/substitution of an ID token",
+ "nullable": true
+ },
+ "clientId": {
+ "type": "string",
+ "description": "The client ID registered with the OAuth 2.0 provider"
+ }
+ },
+ "required": [
+ "provider",
+ "authCode",
+ "redirectUri",
+ "codeVerifier",
+ "clientId"
+ ]
+ },
+ "OAuth2AuthenticateResponse": {
+ "type": "object",
+ "properties": {
+ "oidcToken": {
+ "type": "string",
+ "description": "A Turnkey issued OIDC token to be used with the LoginWithOAuth activity"
+ }
+ },
+ "required": [
+ "oidcToken"
+ ]
+ },
+ "OAuthLoginRequest": {
+ "type": "object",
+ "properties": {
+ "oidcToken": {
+ "type": "string",
+ "description": "Base64 encoded OIDC token"
+ },
+ "publicKey": {
+ "type": "string",
+ "description": "Client-side public key generated by the user, which will be conditionally added to org data based on the validity of the oidc token associated with this request"
+ },
+ "invalidateExisting": {
+ "type": "boolean",
+ "description": "Invalidate all other previously generated Login API keys",
+ "nullable": true
+ },
+ "organizationId": {
+ "type": "string",
+ "description": "Unique identifier for a given Organization. If provided, this organization id will be used directly. If omitted, uses the OIDC token to look up the associated organization id.",
+ "nullable": true
+ }
+ },
+ "required": [
+ "oidcToken",
+ "publicKey"
+ ]
+ },
+ "OAuthLoginResponse": {
+ "type": "object",
+ "properties": {
+ "session": {
+ "type": "string",
+ "description": "Signed JWT containing an expiry, public key, session type, user id, and organization id"
+ }
+ },
+ "required": [
+ "session"
+ ]
+ },
+ "Oauth2Provider": {
+ "type": "string",
+ "enum": [
+ "OAUTH2_PROVIDER_X",
+ "OAUTH2_PROVIDER_DISCORD"
+ ]
+ },
+ "OauthProviderParams": {
+ "type": "object",
+ "properties": {
+ "providerName": {
+ "type": "string",
+ "description": "Human-readable name to identify a Provider."
+ },
+ "oidcToken": {
+ "type": "string",
+ "description": "Base64 encoded OIDC token"
+ }
+ },
+ "required": [
+ "providerName",
+ "oidcToken"
+ ]
+ },
+ "OauthProviderParamsV2": {
+ "type": "object",
+ "properties": {
+ "providerName": {
+ "type": "string",
+ "description": "Human-readable name to identify a Provider."
+ },
+ "oidcToken": {
+ "type": "string",
+ "description": "Base64 encoded OIDC token"
+ },
+ "oidcClaims": {
+ "$ref": "#/components/schemas/OidcClaims"
+ }
+ },
+ "required": [
+ "providerName"
+ ]
+ },
+ "OidcClaims": {
+ "type": "object",
+ "properties": {
+ "iss": {
+ "type": "string",
+ "description": "The issuer identifier from the OIDC token (iss claim)"
+ },
+ "sub": {
+ "type": "string",
+ "description": "The subject identifier from the OIDC token (sub claim)"
+ },
+ "aud": {
+ "type": "string",
+ "description": "The audience from the OIDC token (aud claim)"
+ }
+ },
+ "required": [
+ "iss",
+ "sub",
+ "aud"
+ ]
+ },
+ "OtpLoginRequest": {
+ "type": "object",
+ "properties": {
+ "verificationToken": {
+ "type": "string",
+ "description": "Signed JWT containing a unique id, expiry, verification type, contact. Verification status of a user is updated when the token is consumed (in OTP_LOGIN requests)"
+ },
+ "publicKey": {
+ "type": "string",
+ "description": "Client-side public key generated by the user, which will be conditionally added to org data based on the validity of the verification token"
+ },
+ "invalidateExisting": {
+ "type": "boolean",
+ "description": "Invalidate all other previously generated Login API keys",
+ "nullable": true
+ },
+ "organizationId": {
+ "type": "string",
+ "description": "Unique identifier for a given Organization. If provided, this organization id will be used directly. If omitted, uses the verification token to look up the verified sub-organization based on the contact and verification type.",
+ "nullable": true
+ },
+ "clientSignature": {
+ "$ref": "#/components/schemas/ClientSignature"
+ }
+ },
+ "required": [
+ "verificationToken",
+ "publicKey"
+ ]
+ },
+ "OtpLoginResponse": {
+ "type": "object",
+ "properties": {
+ "session": {
+ "type": "string",
+ "description": "Signed JWT containing an expiry, public key, session type, user id, and organization id"
+ }
+ },
+ "required": [
+ "session"
+ ]
+ },
+ "OtpLoginV2Request": {
+ "type": "object",
+ "properties": {
+ "verificationToken": {
+ "type": "string",
+ "description": "Session containing a unique id, expiry, verification type, contact. Verification status of a user is updated when the token is consumed (in OTP_LOGIN requests)"
+ },
+ "publicKey": {
+ "type": "string",
+ "description": "Client-side public key generated by the user, used as the session public key upon successful login."
+ },
+ "clientSignature": {
+ "$ref": "#/components/schemas/ClientSignature"
+ },
+ "invalidateExisting": {
+ "type": "boolean",
+ "description": "Invalidate all other previously generated Login sessions",
+ "nullable": true
+ },
+ "organizationId": {
+ "type": "string",
+ "description": "Unique identifier for a given Organization. If provided, this organization id will be used directly. If omitted, uses the verification token to look up the verified sub-organization based on the contact and verification type.",
+ "nullable": true
+ }
+ },
+ "required": [
+ "verificationToken",
+ "publicKey",
+ "clientSignature"
+ ]
+ },
+ "OtpLoginV2Response": {
+ "type": "object",
+ "properties": {
+ "session": {
+ "type": "string",
+ "description": "Session containing an expiry, public key, session type, user id, and organization id"
+ }
+ },
+ "required": [
+ "session"
+ ]
+ },
+ "PathFormat": {
+ "type": "string",
+ "enum": [
+ "PATH_FORMAT_BIP32"
+ ]
+ },
+ "SignupRequest": {
+ "type": "object",
+ "properties": {
+ "userEmail": {
+ "type": "string",
+ "nullable": true
+ },
+ "userPhoneNumber": {
+ "type": "string",
+ "nullable": true
+ },
+ "userTag": {
+ "type": "string",
+ "nullable": true
+ },
+ "userName": {
+ "type": "string"
+ },
+ "organizationName": {
+ "type": "string"
+ },
+ "verificationToken": {
+ "type": "string",
+ "nullable": true
+ },
+ "apiKeys": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ApiKeyParamsV2"
+ },
+ "description": "A list of API Key parameters. This field, if not needed, should be an empty array in your request body."
+ },
+ "authenticators": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AuthenticatorParamsV2"
+ },
+ "description": "A list of Authenticator parameters. This field, if not needed, should be an empty array in your request body."
+ },
+ "oauthProviders": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OauthProviderParams"
+ },
+ "description": "A list of Oauth providers. This field, if not needed, should be an empty array in your request body."
+ },
+ "wallet": {
+ "$ref": "#/components/schemas/WalletParams"
+ },
+ "clientSignature": {
+ "$ref": "#/components/schemas/ClientSignature"
+ }
+ },
+ "required": [
+ "apiKeys",
+ "authenticators",
+ "oauthProviders"
+ ]
+ },
+ "SignupResponse": {
+ "type": "object",
+ "properties": {
+ "organizationId": {
+ "type": "string"
+ },
+ "wallet": {
+ "$ref": "#/components/schemas/WalletResult"
+ },
+ "userId": {
+ "type": "string",
+ "description": "Root user ID created for this sub-organization",
+ "title": "User ID"
+ },
+ "appProofs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AppProof"
+ },
+ "description": "A list of App Proofs generated by enclaves during activity execution, providing verifiable attestations of performed operations."
+ }
+ },
+ "required": [
+ "organizationId",
+ "userId"
+ ]
+ },
+ "SignupV2Request": {
+ "type": "object",
+ "properties": {
+ "userEmail": {
+ "type": "string",
+ "nullable": true
+ },
+ "userPhoneNumber": {
+ "type": "string",
+ "nullable": true
+ },
+ "userTag": {
+ "type": "string",
+ "nullable": true
+ },
+ "userName": {
+ "type": "string"
+ },
+ "organizationName": {
+ "type": "string"
+ },
+ "verificationToken": {
+ "type": "string",
+ "nullable": true
+ },
+ "apiKeys": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ApiKeyParamsV2"
+ },
+ "description": "A list of API Key parameters. This field, if not needed, should be an empty array in your request body."
+ },
+ "authenticators": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AuthenticatorParamsV2"
+ },
+ "description": "A list of Authenticator parameters. This field, if not needed, should be an empty array in your request body."
+ },
+ "oauthProviders": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OauthProviderParamsV2"
+ },
+ "description": "A list of Oauth providers. This field, if not needed, should be an empty array in your request body."
+ },
+ "wallet": {
+ "$ref": "#/components/schemas/WalletParams"
+ },
+ "clientSignature": {
+ "$ref": "#/components/schemas/ClientSignature"
+ }
+ },
+ "required": [
+ "apiKeys",
+ "authenticators",
+ "oauthProviders"
+ ]
+ },
+ "SignupV2Response": {
+ "type": "object",
+ "properties": {
+ "organizationId": {
+ "type": "string"
+ },
+ "wallet": {
+ "$ref": "#/components/schemas/WalletResult"
+ },
+ "userId": {
+ "type": "string",
+ "description": "Root user ID created for this sub-organization",
+ "title": "User ID"
+ },
+ "appProofs": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/AppProof"
+ },
+ "description": "A list of App Proofs generated by enclaves during activity execution, providing verifiable attestations of performed operations."
+ }
+ },
+ "required": [
+ "organizationId",
+ "userId"
+ ]
+ },
+ "VerifyOtpRequest": {
+ "type": "object",
+ "properties": {
+ "otpId": {
+ "type": "string",
+ "description": "ID representing the result of an init OTP activity."
+ },
+ "otpCode": {
+ "type": "string",
+ "description": "OTP sent out to a user's contact (email or SMS)"
+ },
+ "publicKey": {
+ "type": "string",
+ "description": "Client-side public key generated by the user, which will be added to the JWT response and verified in subsequent requests via a client proof signature",
+ "nullable": true
+ }
+ },
+ "required": [
+ "otpId",
+ "otpCode"
+ ]
+ },
+ "VerifyOtpResponse": {
+ "type": "object",
+ "properties": {
+ "verificationToken": {
+ "type": "string",
+ "description": "Signed JWT containing a unique id, expiry, verification type, contact. Verification status of a user is updated when the token is consumed (in OTP_LOGIN requests)"
+ }
+ },
+ "required": [
+ "verificationToken"
+ ]
+ },
+ "VerifyOtpV2Request": {
+ "type": "object",
+ "properties": {
+ "otpId": {
+ "type": "string",
+ "description": "ID representing the result of an init OTP activity."
+ },
+ "encryptedOtpBundle": {
+ "type": "string",
+ "description": "Encrypted bundle containing the OTP code and a client-generated public key. Turnkey's secure enclaves will decrypt this bundle, verify the OTP code, and issue a new Verification Token. Encrypted using the target encryption key provided in the INIT_OTP activity result."
+ }
+ },
+ "required": [
+ "otpId",
+ "encryptedOtpBundle"
+ ]
+ },
+ "VerifyOtpV2Response": {
+ "type": "object",
+ "properties": {
+ "verificationToken": {
+ "type": "string",
+ "description": "Verification Token containing a unique id, expiry, verification type, contact signed by Turnkey's enclaves. Verification status of a user is updated when the token is consumed (in OTP_LOGIN requests)"
+ }
+ },
+ "required": [
+ "verificationToken"
+ ]
+ },
+ "WalletAccountParams": {
+ "type": "object",
+ "properties": {
+ "curve": {
+ "$ref": "#/components/schemas/Curve"
+ },
+ "pathFormat": {
+ "$ref": "#/components/schemas/PathFormat"
+ },
+ "path": {
+ "type": "string",
+ "description": "Path used to generate a wallet Account."
+ },
+ "addressFormat": {
+ "$ref": "#/components/schemas/AddressFormat"
+ }
+ },
+ "required": [
+ "curve",
+ "pathFormat",
+ "path",
+ "addressFormat"
+ ]
+ },
+ "WalletParams": {
+ "type": "object",
+ "properties": {
+ "walletName": {
+ "type": "string",
+ "description": "Human-readable name for a Wallet."
+ },
+ "accounts": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/WalletAccountParams"
+ },
+ "description": "A list of wallet Accounts. This field, if not needed, should be an empty array in your request body."
+ },
+ "mnemonicLength": {
+ "type": "integer",
+ "format": "int32",
+ "description": "Length of mnemonic to generate the Wallet seed. Defaults to 12. Accepted values: 12, 15, 18, 21, 24.",
+ "nullable": true
+ }
+ },
+ "required": [
+ "walletName",
+ "accounts"
+ ]
+ },
+ "WalletResult": {
+ "type": "object",
+ "properties": {
+ "walletId": {
+ "type": "string"
+ },
+ "addresses": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "A list of account addresses."
+ }
+ },
+ "required": [
+ "walletId",
+ "addresses"
+ ]
+ },
+ "data.v1.SignatureScheme": {
+ "type": "string",
+ "enum": [
+ "SIGNATURE_SCHEME_EPHEMERAL_KEY_P256"
+ ]
+ }
+ }
+ },
+ "x-original-swagger-version": "2.0"
+}
\ No newline at end of file
diff --git a/scripts/openapi-gen/swagger-to-openapi.ts b/scripts/openapi-gen/swagger-to-openapi.ts
index db41adc8..3183711a 100644
--- a/scripts/openapi-gen/swagger-to-openapi.ts
+++ b/scripts/openapi-gen/swagger-to-openapi.ts
@@ -2,8 +2,19 @@ import * as fs from "fs";
import * as path from "path";
import { convertObj } from "swagger2openapi";
-const SWAGGER_PATH = path.resolve(__dirname, "../../public_api.swagger.json");
-const OUTPUT_PATH = path.resolve(__dirname, "openapi.json");
+const serviceArg = process.argv.find((a: string) => a.startsWith("--service="));
+const service = serviceArg ? serviceArg.split("=")[1] : "turnkey";
+
+const isAuthProxy = service === "auth-proxy";
+
+const SWAGGER_PATH = path.resolve(
+ __dirname,
+ isAuthProxy ? "../../proxy_api.swagger.json" : "../../public_api.swagger.json"
+);
+const OUTPUT_PATH = path.resolve(
+ __dirname,
+ isAuthProxy ? "proxy_api_openapi.json" : "openapi.json"
+);
// Doing some custom key ordering to match the old openapi.json and minimize diffs in initial change
const KEY_ORDERS: Record = {
@@ -46,6 +57,12 @@ function reorderPaths(paths: any): any {
async function main() {
const swagger = JSON.parse(fs.readFileSync(SWAGGER_PATH, "utf-8"));
+ // Inject host/schemes for services that don't declare them in the spec
+ if (isAuthProxy) {
+ swagger.host = "authproxy.turnkey.com";
+ swagger.schemes = ["https"];
+ }
+
const { openapi } = await convertObj(swagger, {
patch: true,
warnOnly: true,
diff --git a/scripts/openapi-gen/utils/cli.ts b/scripts/openapi-gen/utils/cli.ts
index ebeab723..a17668be 100644
--- a/scripts/openapi-gen/utils/cli.ts
+++ b/scripts/openapi-gen/utils/cli.ts
@@ -22,7 +22,11 @@ export interface CliOptions {
requiredOnly?: boolean;
generateMdx?: boolean;
mdxOutputDir?: string;
- mdxAddOnly?: boolean; // Add the new option
+ mdxAddOnly?: boolean;
+ /** Nav group name in docs.json, e.g. "Auth Proxy" */
+ navGroup?: string;
+ /** If true, use the auth-proxy generator (flat output, custom URL/auth header, version deduplication) */
+ authProxy?: boolean;
}
/**
@@ -77,6 +81,15 @@ export function configureCLI(): Command {
"--mdx-add-only",
"Only add new MDX files, do not overwrite existing ones",
false
+ )
+ .option(
+ "--nav-group ",
+ "Navigation group name in docs.json"
+ )
+ .option(
+ "--auth-proxy",
+ "Use the auth-proxy generator (flat output, custom URL/auth header, version deduplication)",
+ false
);
return program;
diff --git a/scripts/openapi-gen/utils/endpoint-parser/string-utils.ts b/scripts/openapi-gen/utils/endpoint-parser/string-utils.ts
index 6f103b35..9e683171 100644
--- a/scripts/openapi-gen/utils/endpoint-parser/string-utils.ts
+++ b/scripts/openapi-gen/utils/endpoint-parser/string-utils.ts
@@ -42,8 +42,10 @@ export function determineEndpointType(path: string): EndpointType {
return "query";
}
- // Default to activity if we can't determine from path
- return "activity";
+ // Default to query for unknown paths — auth proxy endpoints (/v1/*) don't match
+ // either pattern, and the activity parser requires intent/result components that
+ // don't exist in the auth proxy spec, producing zero endpoints if typed as activity.
+ return "query";
}
/**
diff --git a/scripts/openapi-gen/utils/mdx-generator/auth-proxy-generator.ts b/scripts/openapi-gen/utils/mdx-generator/auth-proxy-generator.ts
new file mode 100644
index 00000000..3b4be9ee
--- /dev/null
+++ b/scripts/openapi-gen/utils/mdx-generator/auth-proxy-generator.ts
@@ -0,0 +1,169 @@
+import fs from "fs";
+import path from "path";
+import { ApiEndpoint } from "../endpoint-parser/types";
+import {
+ generateParamMdxRecursive,
+ generateResponseFieldMdxRecursive,
+ generateJsonPayloadRecursive,
+} from "./generator";
+
+const BASE_URL = "https://authproxy.turnkey.com";
+const AUTH_HEADER = "X-Auth-Proxy-Config-Id";
+
+// --- Helper: Generate curl request example ---
+function generateRequestExample(endpoint: ApiEndpoint): string {
+ const url = `${BASE_URL}${endpoint.path}`;
+ const fields = endpoint.requestBody?.fields || [];
+ const dataPayloadObject = generateJsonPayloadRecursive(fields, endpoint.path);
+
+ const dataPayloadString = JSON.stringify(
+ dataPayloadObject,
+ (_key, value) => (typeof value === "bigint" ? value.toString() : value),
+ 4
+ );
+ const escapedDataPayloadString = dataPayloadString.replace(/'/g, "'\\''");
+
+ const curlCommand =
+ "```bash title=\"cURL\"\n" +
+ "curl --request POST \\\n" +
+ ` --url ${url} \\\n` +
+ " --header 'Accept: application/json' \\\n" +
+ " --header 'Content-Type: application/json' \\\n" +
+ ` --header "${AUTH_HEADER}: (see Authorizations)" \\\n` +
+ ` --data '${escapedDataPayloadString}'\n` +
+ "```";
+
+ return `\n\n${curlCommand}\n\n `;
+}
+
+// --- Helper: Generate response example ---
+function generateResponseExample(endpoint: ApiEndpoint): string {
+ const successResponse = endpoint.responses?.find(
+ (res) => res.statusCode === 200
+ );
+
+ let resultPayload: Record;
+ if (successResponse?.fields) {
+ resultPayload = generateJsonPayloadRecursive(
+ successResponse.fields,
+ endpoint.path
+ );
+ } else {
+ resultPayload = { "": "" };
+ }
+
+ const responseJsonString = JSON.stringify(
+ resultPayload,
+ (_key, value) => (typeof value === "bigint" ? value.toString() : value),
+ 2
+ );
+
+ return `\n\n\`\`\`json 200\n${responseJsonString}\n\`\`\`\n\n `;
+}
+
+// --- Main MDX content generation for auth proxy endpoints ---
+function generateAuthProxyMdxContent(endpoint: ApiEndpoint): string {
+ let mdxContent = `---
+title: "${endpoint.title || "API Endpoint"}"
+description: "${endpoint.description || "API endpoint documentation"}"
+---
+
+import { H3Bordered } from "/snippets/h3-bordered.mdx";
+import { NestedParam } from "/snippets/nested-param.mdx";
+
+
+
+
+
POST
+
${BASE_URL}${endpoint.path}
+
+
+
+
+
+
+
+ Your Auth Proxy config ID, found in **Dashboard → AUTH**. See [Auth Proxy reference](/reference/auth-proxy) for setup.
+
+
+`;
+
+ // Request body
+ if (endpoint.requestBody?.fields && endpoint.requestBody.fields.length > 0) {
+ mdxContent += ` \n\n`;
+ for (const field of endpoint.requestBody.fields) {
+ mdxContent += generateParamMdxRecursive(field.name, field, "");
+ }
+ }
+
+ // Response fields
+ const successResponse = endpoint.responses?.find(
+ (res) => res.statusCode === 200
+ );
+ if (successResponse?.fields && successResponse.fields.length > 0) {
+ mdxContent += `\n \n`;
+ mdxContent += `A successful response returns the following fields:\n\n`;
+ for (const field of successResponse.fields) {
+ mdxContent += generateResponseFieldMdxRecursive(field);
+ }
+ } else {
+ mdxContent += `\n{/* No explicit 200 response schema defined. */}\n`;
+ }
+
+ // Examples
+ mdxContent += `\n${generateRequestExample(endpoint)}\n`;
+ mdxContent += `\n${generateResponseExample(endpoint)}\n`;
+
+ return mdxContent;
+}
+
+// --- Main file generation function for auth proxy endpoints ---
+// Uses the path slug (without version suffix) as the filename so that
+// e.g. /v1/otp_init_v2 → otp-init.mdx (concept name, not version name).
+export function generateAuthProxyMdxFile(
+ endpoint: ApiEndpoint,
+ baseOutputDir: string,
+ addOnly: boolean = false
+): string | null {
+ if (!endpoint.title || !endpoint.path) {
+ console.warn(
+ `Skipping MDX generation for endpoint (Title: ${endpoint.title}, Path: ${endpoint.path}) due to missing title or path.`
+ );
+ return null;
+ }
+
+ const slug = (endpoint.path.split("/").filter(Boolean).pop() || endpoint.title)
+ .replace(/_v\d+$/, ""); // strip version suffix
+
+ const kebabCaseSlug = slug
+ .toLowerCase()
+ .replace(/[\s_]+/g, "-")
+ .replace(/[^a-z0-9-]/g, "")
+ .replace(/-+/g, "-")
+ .replace(/^-+|-+$/g, "");
+
+ const filename = `${kebabCaseSlug}.mdx`;
+ const outputPath = path.join(baseOutputDir, filename);
+
+ if (!fs.existsSync(baseOutputDir)) {
+ fs.mkdirSync(baseOutputDir, { recursive: true });
+ }
+
+ if (addOnly && fs.existsSync(outputPath)) {
+ console.log(`Skipping existing file (addOnly=true): ${outputPath}`);
+ return kebabCaseSlug;
+ }
+
+ try {
+ const mdxContent = generateAuthProxyMdxContent(endpoint);
+ fs.writeFileSync(outputPath, mdxContent);
+ console.log(`Generated MDX file: ${outputPath}`);
+ return kebabCaseSlug;
+ } catch (error: any) {
+ console.error(
+ `Error generating auth proxy MDX file for endpoint "${endpoint.title}": ${error.message}`
+ );
+ if (error.stack) console.error(error.stack);
+ return null;
+ }
+}
diff --git a/scripts/openapi-gen/utils/mdx-generator/generator.ts b/scripts/openapi-gen/utils/mdx-generator/generator.ts
index 5809b938..f25b9ec9 100644
--- a/scripts/openapi-gen/utils/mdx-generator/generator.ts
+++ b/scripts/openapi-gen/utils/mdx-generator/generator.ts
@@ -3,7 +3,7 @@ import path from "path";
import { ApiEndpoint, ApiField, EnumOption } from "../endpoint-parser/types";
// --- Helper: Escape HTML Chars ---
-function escapeHtmlChars(text: string): string {
+export function escapeHtmlChars(text: string): string {
if (!text) return "";
return text
.replace(/ for top-level, imported for nested.
-function generateResponseFieldMdxRecursive(
+export function generateResponseFieldMdxRecursive(
field: ApiField,
parentKey: string = ""
): string {
@@ -240,7 +240,7 @@ function generateResponseFieldMdxRecursive(
}
// --- Helper: Generate JSON Payload Object ---
-function generateJsonPayloadRecursive(
+export function generateJsonPayloadRecursive(
fields: ApiField[] | undefined,
endpointPath?: string,
parentPath: string = ""
@@ -316,7 +316,7 @@ function generateJsonPayloadRecursive(
if (fieldDetails.isEnum && fieldDetails.options.length > 0) {
// Simple Enum
let enumValue = `<${fieldDetails.options[0].value}>`;
-
+
// Special handling for credential type in get-api-key and get-api-keys endpoints.
// Current behavior: for enum types, we grab the first (which defaults to CREDENTIAL_TYPE_WEBAUTHN_AUTHENTICATOR).
// However, for `get_api_key` and `get_api_keys` endpoints, we want to use CREDENTIAL_TYPE_API_KEY_P256.
@@ -327,7 +327,7 @@ function generateJsonPayloadRecursive(
) {
enumValue = "";
}
-
+
value = enumValue;
} else {
// Simple Type (non-enum)
@@ -362,13 +362,13 @@ function getSdkMethodName(endpoint: ApiEndpoint): string {
return op.slice(0, 1).toLowerCase() + op.slice(1);
}
const path = endpoint.path || "";
-
+
// Extract the last part of the path (e.g., "approve_activity" from "/public/v1/submit/approve_activity")
const pathParts = path.split("/").filter(Boolean);
const lastPart = pathParts[pathParts.length - 1];
-
+
if (!lastPart) return "unknownMethod";
-
+
// Convert snake_case to camelCase
return lastPart.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}
@@ -379,10 +379,10 @@ function generateSdkParameterValue(field: ApiField, indent: number = 2): string
const fieldName = field.name;
const fieldType = field.type;
const description = field.description ? ` // ${field.description}` : "";
-
+
// Check if this is an enum field
const { isEnum, options } = getEnumDetails(field);
-
+
if (isEnum && options.length > 0) {
// For enum fields, show the first option as an example
const firstOption = options[0].value;
@@ -437,7 +437,7 @@ function generateSdkParameterValue(field: ApiField, indent: number = 2): string
return `${indentStr}${fieldName}: { /* object */ }${description}`;
}
}
-
+
return `${indentStr}${fieldName}: ""${description}`;
}
@@ -445,7 +445,7 @@ function generateSdkParameterValue(field: ApiField, indent: number = 2): string
function generateSdkParameters(endpoint: ApiEndpoint): string {
const fields = endpoint.requestBody?.fields || [];
const parametersWrapper = fields.find((f) => f.name === "parameters");
-
+
if (parametersWrapper) {
if (parametersWrapper.childFields && parametersWrapper.childFields.length > 0) {
// For activity endpoints, extract parameters from the parameters wrapper only
@@ -462,7 +462,7 @@ function generateSdkParameters(endpoint: ApiEndpoint): string {
// For query endpoints, use all top-level fields (excluding only type and timestampMs)
const commonFields = ["type", "timestampMs"]; // organizationId should be included for queries
const endpointSpecificFields = fields.filter(f => !commonFields.includes(f.name));
-
+
if (endpointSpecificFields.length === 0) {
return ""; // No endpoint-specific parameters
}
@@ -515,7 +515,7 @@ function generateRequestExample(endpoint: ApiEndpoint): string {
// Stringify carefully, handle potential BigInts if they arise
const dataPayloadString = JSON.stringify(
dataPayloadObject,
- (key, value) => (typeof value === "bigint" ? value.toString() : value),
+ (_key, value) => (typeof value === "bigint" ? value.toString() : value),
4
); // Pretty print with 4 spaces
@@ -538,7 +538,7 @@ function generateRequestExample(endpoint: ApiEndpoint): string {
const jsParams = sdkParameters.trim() === "" ? "{}" : `{
${sdkParameters}
}`;
- const javascriptExample =
+ const javascriptExample =
"```javascript title=\"JavaScript\"\n" +
"import { Turnkey } from \"@turnkey/sdk-server\";\n\n" +
"const turnkeyClient = new Turnkey({\n" +
@@ -598,14 +598,14 @@ function generateResponseExample(endpoint: ApiEndpoint): string {
};
responseJsonString = JSON.stringify(
responsePayloadObject,
- (key, value) => (typeof value === "bigint" ? value.toString() : value),
+ (_key, value) => (typeof value === "bigint" ? value.toString() : value),
2
); // 2-space indent
} else {
// Query endpoints: just the result object
responseJsonString = JSON.stringify(
resultPayload,
- (key, value) => (typeof value === "bigint" ? value.toString() : value),
+ (_key, value) => (typeof value === "bigint" ? value.toString() : value),
2
);
}
@@ -750,4 +750,4 @@ import { EndpointPath } from "/snippets/api/endpoint.mdx";
}
return mdxContent;
-}
\ No newline at end of file
+}
diff --git a/scripts/openapi-gen/utils/mdx-generator/index.ts b/scripts/openapi-gen/utils/mdx-generator/index.ts
index 5113b178..eff5e850 100644
--- a/scripts/openapi-gen/utils/mdx-generator/index.ts
+++ b/scripts/openapi-gen/utils/mdx-generator/index.ts
@@ -2,3 +2,4 @@
* MDX Generator module exports
*/
export * from './generator';
+export * from './auth-proxy-generator';