Integration code examples
This guide provides code examples for access tokens, API calls, PKCE sign-in, and webhooks. Create an Application and enable the required API access in the Developer Console before integration.
Examples use placeholders. Copy the issuer, Client ID, API address, audience, and scopes from the current Application into your server configuration.
1. Choose the caller identity
| Need | Choose | Token flow | Secret |
|---|---|---|---|
| Background jobs, scheduled sync, service-to-service calls | Application identity permissions | Client Credentials | Client Secret on the server only |
| User sign-in and calls on behalf of that user | User identity permissions | Authorization Code + PKCE (S256) | No Client Secret |
If both are needed, enable and request them separately in the same Application. The identities must not share Clients, scopes, or tokens.
2. Copy values from the Console
| Console page | Copy | Used for |
|---|---|---|
| OAuth/OIDC | Issuer, Client ID, callback URLs | Sign-in and token requests |
| APIs and permissions | API Base, audience, approved scopes, paths | Backend API calls |
| Events and Webhooks | Webhook URL, event types, secret delivery status | Receiving events |
Copy only values approved for the current environment and identity. User identity has no Client Secret; an application Secret belongs only in a server-side Secret Manager.
3. Server environment template
GOODVISION_IDENTITY_ISSUER=<copy from OAuth/OIDC>
GOODVISION_TOKEN_ENDPOINT=<Discovery token_endpoint>
GOODVISION_AUTHORIZATION_ENDPOINT=<Discovery authorization_endpoint>
GOODVISION_APPLICATION_ID=<current Application ID>
GOODVISION_API_BASE=<copy from APIs and permissions>
GOODVISION_AUDIENCE=<copy from APIs and permissions>
GOODVISION_SCOPES=<approved least-privilege scopes, separated by spaces>
# Only application identity needs these; never put them in browser code.
GOODVISION_SERVICE_CLIENT_ID=<application identity Client ID>
GOODVISION_SERVICE_CLIENT_SECRET=<application identity Client Secret>For user identity, use GOODVISION_USER_CLIENT_ID, the registered GOODVISION_REDIRECT_URI, and GOODVISION_POST_LOGOUT_URI; do not add a Client Secret.
Read Discovery from the exact GOODVISION_IDENTITY_ISSUER plus /.well-known/openid-configuration. Verify issuer and use its token_endpoint, authorization_endpoint and, when needed, end_session_endpoint. The endpoint variables above store these returned URLs; do not guess fixed paths.
For recommended event reception, run the Node.js 22 long-connection example. HTTPS reception and synchronous callbacks remain separate choices.
4. Application identity: obtain a token
Run this only in the third-party backend. A browser must not run Client Credentials or receive a Client Secret.
curl --request POST \
--url "${GOODVISION_TOKEN_ENDPOINT}" \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=client_credentials' \
--data-urlencode "client_id=${GOODVISION_SERVICE_CLIENT_ID}" \
--data-urlencode "client_secret=${GOODVISION_SERVICE_CLIENT_SECRET}"Use the short-lived token to call the API, and check its issuer, target API, expiry, and scopes. Reacquire it after expiry; never extend it locally.
5. Call an API
Get GOODVISION_API_BASE and endpoint paths from the current application’s enabled API product. The following example uses SaaSdemo Sandbox commerce APIs:
curl --request GET \
--url "${GOODVISION_API_BASE}/open-platform/v1/products" \
--header "authorization: Bearer ${GOODVISION_ACCESS_TOKEN}" \
--header "X-GV-Application-ID: ${GOODVISION_APPLICATION_ID}"| API | Minimum scope |
|---|---|
GET /open-platform/v1/products | commerce.catalog.read |
POST /open-platform/v1/checkout-sessions | commerce.checkout.create |
GET /open-platform/v1/operations/{id} | commerce.checkout.read |
GET /open-platform/v1/subscriptions, GET /open-platform/v1/subscriptions/{id} | commerce.subscription.read |
POST /open-platform/v1/subscriptions/{id}:cancel, :change-plan | commerce.subscription.manage |
The matching API Product, audience, and scopes must be approved before a route can be called. Every write includes a unique Idempotency-Key; updates also include the current If-Match version.
The third-party browser or request parameters cannot override amount, currency, payment route, organization, or return URL. Subscription, payment, billing, and entitlement pages remain in the GoodVision customer workspace.
6. User sign-in: PKCE
User identity is for browser sign-in. The backend creates a one-time code_verifier and state, then redirects the user to:
${GOODVISION_AUTHORIZATION_ENDPOINT}
?client_id=${GOODVISION_USER_CLIENT_ID}
&response_type=code
&scope=openid%20profile%20email
&redirect_uri=${GOODVISION_REDIRECT_URI}
&code_challenge=${GOODVISION_CODE_CHALLENGE}
&code_challenge_method=S256
&state=${GOODVISION_STATE}Exchange the code on the backend, validate state, PKCE, issuer, target API, and expiry, then keep the sign-in state in an HttpOnly session. The browser receives only a session cookie; user identity has no Client Secret.
7. Receive Webhooks
The Webhook Secret is delivered through the secure method shown by the Console and is not included in ordinary exports. The receiver should:
- Check the time window and atomically reserve the nonce, then compute HMAC-SHA256 over raw HTTP bytes and compare in constant time.
- Validate and remove the Verification Token in both plaintext and encrypted modes, then check the event schema and integration scope.
- Commit a durable Inbox idempotently by
event_idbefore returning2xx; return503on storage failure and never repeat business work. - Handle ordering by resource version and reconcile gaps through an authorized status API; the platform decides delivery retries from the response.
See Receive webhooks for complete headers, Body, 204 responses, encryption, and a runnable signature script. Complete cancellation, plan-change, and expiry JSON examples are in Subscription lifecycle.
8. Node.js, Go, and Python examples
This section uses standard libraries to demonstrate direct HTTP calls without an SDK. TypeScript/Node.js projects can install the official @goodvision/sdk; Go and Python projects can use the direct HTTP examples below. Production code still needs error handling, timeouts, and retries.
Node.js (built-in fetch)
const tokenResponse = await fetch(
process.env.GOODVISION_TOKEN_ENDPOINT,
{
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.GOODVISION_SERVICE_CLIENT_ID,
client_secret: process.env.GOODVISION_SERVICE_CLIENT_SECRET,
}),
},
);
const { access_token: accessToken } = await tokenResponse.json();
const response = await fetch(`${process.env.GOODVISION_API_BASE}/open-platform/v1/products`, {
headers: {
authorization: `Bearer ${accessToken}`,
"X-GV-Application-ID": process.env.GOODVISION_APPLICATION_ID,
},
});Go (standard library)
form := url.Values{
"grant_type": {"client_credentials"},
"client_id": {os.Getenv("GOODVISION_SERVICE_CLIENT_ID")},
"client_secret": {os.Getenv("GOODVISION_SERVICE_CLIENT_SECRET")},
}
req, _ := http.NewRequest("POST", os.Getenv("GOODVISION_TOKEN_ENDPOINT"), strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
// Decode access_token, then call the current API Product.Python (standard library)
import os
import urllib.parse
import urllib.request
body = urllib.parse.urlencode({
"grant_type": "client_credentials",
"client_id": os.environ["GOODVISION_SERVICE_CLIENT_ID"],
"client_secret": os.environ["GOODVISION_SERVICE_CLIENT_SECRET"],
}).encode()
token_request = urllib.request.Request(
os.environ["GOODVISION_TOKEN_ENDPOINT"],
data=body,
headers={"content-type": "application/x-www-form-urlencoded"},
)
# Read access_token only in the backend, then call the current API Product.SDK support
The official TypeScript/Node.js SDK is available from npm for application code. Go and Python SDKs are not released yet; those projects can use the direct HTTP examples on this page. Whether you use the SDK or direct HTTP, do not write tokens, secrets, cookies, or request bodies to logs.
Notes
- Keep tokens, Client Secrets, and Webhook Secrets on the server; never put them in frontend code, URLs, logs, or tickets.
- Use one caller identity per request; user and application Clients, scopes, and tokens are not interchangeable.
- Treat plans, subscriptions, payments, billing, and entitlements as platform state; SaaSdemo does not copy them.
- TypeScript/Node.js projects can use the official SDK; other languages should use the Console configuration, OpenAPI/event contract, and these templates.
Continue with Authentication and authorization, API Products, and the SaaSdemo integration example.
User and space after login
After validating identity, your server sends the initial {} request described in Users and customer spaces. The platform handles default space and user correlation. Do not hardcode the developer business for business requests; configure other APIs, events and commerce as needed.