The operator portal and machine API are separate trust boundaries. Browsers use passkeys on integration.boringordertracker.com; only api.integration.boringordertracker.com requests a client certificate. Never send a private key, P12 password, one-time claim token, or live certificate to BOT support, source control, a CI log, or an AI assistant.
Sandbox and production have different clients, scopes, quotas and certificates. The certificate selects the environment; there is no environment request header or query parameter. A sandbox certificate cannot read production data, and a production certificate cannot read sandbox fixtures.
Recommended: create a private key and CSR locally#
In the integration detail page, open Certificates and copy the displayed Required CSR common name. It is derived from the already-created client and has this exact form:
bot:<environment>:<client-id>
Copy the complete value from the portal. Do not substitute the integration ID, slug, operator ID, or a self-selected name. A client ID is not a secret, but the private key is.
The following commands create an encrypted EC P-256 key and a SHA-256 PKCS#10 CSR. Run them on the server or secret-management workstation that will retain the key:
umask 077
export BOT_CSR_CN='bot:sandbox:PASTE-THE-EXACT-CLIENT-ID-FROM-THE-PORTAL'
openssl genpkey \
-algorithm EC \
-pkeyopt ec_paramgen_curve:prime256v1 \
-aes-256-cbc \
-out bot-sandbox-client-key.pem
openssl req \
-new \
-sha256 \
-key bot-sandbox-client-key.pem \
-subj "/CN=${BOT_CSR_CN}" \
-out bot-sandbox-client.csr.pem
OpenSSL asks for a private-key passphrase; store it in the service's secret manager. For an unattended service, inject it through the process secret store or TLS runtime rather than placing it in a command line, image, repository, or world-readable configuration file.
RSA is supported when EC is unavailable. Use 3072–8192 bits; this example uses an encrypted 3072-bit key:
umask 077
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:3072 \
-aes-256-cbc \
-out bot-sandbox-client-key.pem
openssl req \
-new \
-sha256 \
-key bot-sandbox-client-key.pem \
-subj "/CN=${BOT_CSR_CN}" \
-out bot-sandbox-client.csr.pem
BOT accepts RSA 3072–8192 and EC P-256/P-384 public keys. Before uploading the CSR, verify its self-signature and inspect the exact subject and public key:
openssl req -in bot-sandbox-client.csr.pem -noout -verify
openssl req -in bot-sandbox-client.csr.pem -noout -subject -nameopt RFC2253
openssl req -in bot-sandbox-client.csr.pem -noout -text
The subject output must contain exactly the portal value as its CN. The text output must show the intended key algorithm/size. Upload only bot-sandbox-client.csr.pem; never upload the private-key file. Certificate issuance is asynchronous because only the private signing worker can access the intermediate CA.
Verify the issued certificate#
The portal's certificate download is a PEM bundle containing the issued leaf certificate first, followed by the public CA chain. It never contains a private key. Save the download beside, but with different permissions from, the private key and inspect it:
export BOT_CERT=/secure/path/bot-integration-certificate-chain.pem
export BOT_KEY=/secure/path/bot-sandbox-client-key.pem
openssl x509 -in "$BOT_CERT" \
-noout -subject -issuer -serial -dates -fingerprint -sha256
openssl crl2pkcs7 -nocrl -certfile "$BOT_CERT" \
| openssl pkcs7 -print_certs -noout
Confirm that the leaf subject is bound to the same portal common name, its validity window is expected, and the bundle contains only the expected BOT certificates. Then prove that the certificate and retained private key contain the same public key:
openssl pkey -in "$BOT_KEY" -pubout -outform DER \
| openssl dgst -sha256
openssl x509 -in "$BOT_CERT" -pubkey -noout \
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256
The two SHA-256 lines must be identical. This is a key-match check, not a replacement for TLS chain validation. curl and the operating system must still validate the API server certificate normally, and BOT validates the presented client chain plus its current database/CRL status.
Test the binding before using any data endpoint:
export BOT_API='https://api.integration.boringordertracker.com/v1'
curl --fail-with-body \
--proto '=https' \
--tlsv1.2 \
--cert "$BOT_CERT" \
--key "$BOT_KEY" \
"$BOT_API/capabilities"
For sandbox, assert that environment is sandbox. For production, assert the expected integration_status, exact scopes and quotas, and production_data_enabled=true before calling data endpoints. Production certificates can be prepared after approval, but production data stays blocked until the independent mobile-consent, legal and operational gates are enabled.
One-time encrypted P12 comfort flow#
The alternative portal flow generates an EC P-256 key only in private worker memory and exports an encrypted P12. A fresh passkey confirmation creates a one-time claim token; only its HMAC is stored. Keep that token until issuance is ready, then claim the package in the portal within 15 minutes. The browser downloads the P12 and displays its password once. The response is no-store, the claim is atomically consumed, and the encrypted server artifact is removed. Unencrypted private keys are never persisted by BOT.
Inspect a claimed package locally without extracting its private key:
export BOT_P12=/secure/path/bot-integration-certificate.p12
openssl pkcs12 -in "$BOT_P12" -info -noout
curl can use the P12 directly. Omitting the password from --cert avoids putting it in shell history or the process list; use curl's interactive prompt for a manual smoke test:
curl --fail-with-body \
--proto '=https' \
--tlsv1.2 \
--cert-type P12 \
--cert "$BOT_P12" \
"$BOT_API/capabilities"
For unattended operation, load the P12 password from the service's secret manager using the TLS client's protected configuration mechanism. Do not append the password to --cert, commit it to a curl config, or export it from a long-lived interactive shell.
Certificate rotation#
Certificates are valid for 90 days. Begin rotation before the expiry shown by openssl x509 -dates; do not wait for the final day. A client may have at most two active certificates per environment, with a maximum 14-day overlap. Rate limits are integration-wide across both certificates, so rotation does not increase quota.
Use this sequence for a zero-downtime rotation:
- In the correct environment, create a new private key/CSR with the exact same portal-displayed client common name, or request a new one-time P12.
- Keep the existing certificate active while issuance completes.
- Validate the new certificate, key match, expiry and chain as above.
- Deploy the new credential to one instance/canary and call
GET /v1/capabilitiesthrough the normal production network path. - Confirm environment, integration status, scopes and quotas, then switch all callers to the new credential and observe successful requests.
- Revoke the old certificate in the portal before the 14-day overlap ends.
- Remove the old key from every runtime and secret store according to the operator's auditable key-destruction procedure.
Do not reuse private keys between sandbox and production or between separate integrations.
Revocation and incident response#
Revoke a certificate immediately if its private key, P12, password, host, CI secret, or backup may have been exposed, or when the credential is no longer in use. Portal revocation changes backend authorization immediately and queues the serial for CA/CRL publication; a certificate that still passes edge TLS must also pass the backend's active-client, environment, integration, serial, fingerprint, status and scope checks.
After revocation:
- stop every process using the affected credential;
- remove it from runtime secret stores and rotate any copied secret material;
- issue a fresh key/certificate, rather than reusing the suspect private key;
- test
GET /v1/capabilitieswith the replacement; - review portal audit/usage information and contact BOT support with the public certificate ID, serial/fingerprint and time window—never the private key or P12 password.
Suspending or retiring an integration is broader than revoking one certificate: it stops production authorization for the integration. Keep portal passkeys and recovery codes available through a separate incident-recovery path so a lost API credential never becomes a lost operator account.