POST
/oauth/installation-exchangeExchange a one-time partner installation authorization code
Server-side exchange for an authorization code issued by the Club Portal. The exact redirect_uri and PKCE S256 verifier must match the owner-approved request. A successful exchange provisions one environment- and club-bound ZITADEL M2M principal; it never returns a private key or browser token.
Öffentlicher Code-Exchange
Request
curl --request POST \
'https://sandbox-api.padel-lab.crewio.co/v1/club/oauth/installation-exchange' \
--header 'Idempotency-Key: 018f-unique-request-key' \
--header 'Content-Type: application/json' \
--data '{
"grant_type": "authorization_code",
"code": "plc_auth_code_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"code_verifier": "plc-verifier-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdef",
"redirect_uri": "https://partner.example/callback"
}'Parameter
Idempotency-KeyheaderstringrequiredUnique key retained for 24 hours on authenticated mutation endpoints. The installation-exchange binding is retained permanently with its consumed authorization code. Reuse with different request content returns IDEMPOTENCY_CONFLICT.
Request Body
PartnerInstallationExchangeInputrequired{
"grant_type": "authorization_code",
"code": "plc_auth_code_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"code_verifier": "plc-verifier-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdef",
"redirect_uri": "https://partner.example/callback"
}JSON Schema anzeigen
{
"type": "object",
"additionalProperties": false,
"required": [
"grant_type",
"code",
"code_verifier",
"redirect_uri"
],
"properties": {
"grant_type": {
"const": "authorization_code"
},
"code": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{40,200}$"
},
"code_verifier": {
"type": "string",
"minLength": 43,
"maxLength": 128,
"pattern": "^[A-Za-z0-9._~-]+$"
},
"redirect_uri": {
"type": "string",
"format": "uri",
"maxLength": 2048
}
}
}Responses
200Installation principal provisionedPartnerInstallationExchangeResponse400Malformed JSON or a syntactically valid request that violates a path, query, header or JSON-body constraint.ErrorResponse403Installation lacks the required scope or is inactiveErrorResponse409Version, idempotency, mapping, state or resource-limit conflictErrorResponse429Rate limit exceededErrorResponse500An unexpected runtime error occurred; retry only when the operation is safe or idempotent.ErrorResponse503A required authorization, tenant-safety or rate-limit dependency is unavailableErrorResponseResponse Schemas
PartnerInstallationExchangeResponseStatus 200
{
"type": "object",
"additionalProperties": false,
"required": [
"data"
],
"properties": {
"data": {
"type": "object",
"additionalProperties": false,
"required": [
"installation_id",
"organization_id",
"application_id",
"environment",
"scopes",
"oauth_subject",
"credential_key_id",
"nonce",
"api_base_url",
"oauth_issuer",
"oauth_token_url",
"oauth_audience",
"oauth_audience_scope"
],
"properties": {
"installation_id": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Opaque stable identifier"
},
"organization_id": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Opaque stable identifier"
},
"application_id": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Opaque stable identifier"
},
"environment": {
"type": "string",
"enum": [
"SANDBOX",
"PRODUCTION"
]
},
"scopes": {
"type": "array",
"items": {
"type": "string",
"enum": [
"club:read",
"locations:read",
"courts:read",
"bookings:read",
"bookings:write",
"participants:read",
"participants:write",
"sessions:read",
"scores:read",
"events:read",
"webhooks:manage"
]
}
},
"oauth_subject": {
"type": "string"
},
"credential_key_id": {
"type": "string",
"minLength": 1
},
"nonce": {
"type": "string"
},
"api_base_url": {
"type": "string",
"format": "uri",
"description": "Environment-specific Club API base URL. This exchange response is authoritative."
},
"oauth_issuer": {
"type": "string",
"format": "uri",
"description": "Environment-specific ZITADEL issuer used as the JWT assertion audience."
},
"oauth_token_url": {
"type": "string",
"format": "uri",
"description": "Environment-specific ZITADEL token endpoint."
},
"oauth_audience": {
"type": "string",
"minLength": 1,
"description": "Audience required on Club API access tokens."
},
"oauth_audience_scope": {
"type": "string",
"pattern": "^urn:zitadel:iam:org:project:id:[A-Za-z0-9_-]+:aud$",
"description": "ZITADEL project audience scope sent alongside openid in the token request."
}
}
}
}
}ErrorResponseStatus 400, 403, 409, 429, 500, 503
{
"type": "object",
"additionalProperties": false,
"required": [
"error"
],
"properties": {
"error": {
"type": "object",
"additionalProperties": false,
"required": [
"code",
"message",
"request_id"
],
"properties": {
"code": {
"type": "string",
"description": "Stable machine-readable code. Clients must tolerate unknown codes within the same HTTP class.",
"examples": [
"invalid_request",
"invalid_cursor",
"IDEMPOTENCY_KEY_REQUIRED",
"unsafe_webhook_url",
"authentication_required",
"invalid_token",
"insufficient_scope",
"installation_inactive",
"not_found",
"VERSION_CONFLICT",
"UNMAPPED_COURT",
"IDEMPOTENCY_CONFLICT",
"IDEMPOTENCY_IN_PROGRESS",
"endpoint_limit_reached",
"rate_limited",
"internal_error",
"idempotency_replay_failed",
"authorization_unavailable",
"rate_limit_unavailable"
]
},
"message": {
"type": "string"
},
"request_id": {
"type": "string"
},
"details": {}
}
}
}
}