Providers & BYOK
PickLayer routes every AI call to an upstream provider (OpenRouter, OpenAI, Anthropic, AWS, Azure, Deepgram, etc.). By default your application uses PickLayer’s managed credentials. Bring Your Own Key (BYOK) lets you substitute your own upstream API keys so that traffic is attributed to your accounts.
Managed vs BYOK
| Managed | BYOK | |
|---|---|---|
| Setup | None — works out of the box | Configure credentials per-application |
| Billing | PickLayer billing includes provider cost + markup | You pay upstream providers directly; PickLayer markup applies on top of zero provider cost |
| Rate limits | Shared PickLayer upstream quota | Your own upstream quota |
| Data isolation | Traffic flows through PickLayer’s upstream accounts | Traffic flows through your accounts |
| Best for | Getting started, prototyping | Production apps, negotiated rates, high quota needs |
When to switch to BYOK
- You have negotiated rates with a provider and want to apply them.
- You need higher rate limits than PickLayer’s shared pool provides.
- Your compliance requirements mandate that all upstream API traffic originate from your own accounts.
- You want to use a provider or deployment not available on the managed tier (e.g. a private Azure OpenAI deployment).
How credential encryption works
When you enter a secret field (API key, secret access key, subscription key) in the console or via the POST /providers/upsert-credentials API:
- The browser retrieves PickLayer’s relay public key.
- The secret is encrypted in the browser with RSA-OAEP (SHA-256) before any network transmission. The plaintext never leaves your device.
- The ciphertext is stored in PickLayer’s database. It is write-only — it cannot be read back via GraphQL or the API.
- At call time, the AI service decrypts the ciphertext server-side using the corresponding private key, uses the secret to call the upstream provider, then discards it.
Non-secret fields (region, deployment name, voice, etc.) are stored as plain text metadata and are readable via the config_safe field on the provider record.
Configuring providers
Via the console
- Open Settings → Provider API Keys in the builder console.
- Click Add provider and select a preset.
- Fill in the required fields. Secret fields are encrypted before save.
- Click Save. The provider becomes active immediately.
Via the API
Use POST /providers/upsert-credentials. See the BUILDER_API_REFERENCE §4.2 for the full parameter reference including how to encrypt secret fields programmatically.
# 1. Fetch the relay public key (no auth required)
PUB=$(curl -s https://api.picklayer.ai/providers/relay-public-key | jq -r '.data.publicKey')
# 2. Encrypt your secret (example using node)
CIPHER=$(node -e "
const crypto = require('crypto');
const key = crypto.createPublicKey('$PUB');
const enc = crypto.publicEncrypt({ key, oaepHash: 'sha256', padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }, Buffer.from(process.env.OPENAI_KEY));
console.log(enc.toString('base64'));
")
# 3. Upsert the provider
curl -X POST https://api.picklayer.ai/providers/upsert-credentials \
-H "Authorization: Bearer $ORG_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"orgId\": \"$ORG_ID\",
\"appId\": \"$APP_ID\",
\"presetId\": \"openai\",
\"name\": \"OpenAI production\",
\"fields\": {
\"apiKey\": { \"ciphertext\": \"$CIPHER\" },
\"organizationId\": { \"value\": \"org-...\" }
}
}"Available providers
| Preset | Provider | Key capabilities |
|---|---|---|
openrouter | OpenRouter | chat, vision, embeddings |
openai | OpenAI | chat, embeddings, TTS, STT, vision |
xai | xAI (Grok) | chat, responses, vision, tools, web search |
anthropic | Anthropic Claude | chat, responses, vision, tools, reasoning |
gemini | Google Gemini | chat, responses, vision, tools, web search |
bedrock | Amazon Bedrock | chat, responses, vision, tools, reasoning |
aws-polly | Amazon Polly | TTS |
aws-transcribe | Amazon Transcribe | STT |
azure-openai | Azure OpenAI | chat, embeddings |
azure-speech | Azure Speech | TTS, STT |
deepgram | Deepgram | STT, TTS |
Use GET /providers/presets (no auth) to retrieve the current list with per-field definitions including which fields are secret vs plain text.
Listing and updating providers
# List all providers for an application (secrets hidden)
query {
providers(where: { application_id: { _eq: "<appId>" } }) {
id name type enabled config_safe created_at
}
}
# Disable a provider
mutation {
update_providers_by_pk(pk_columns: { id: "<providerId>" }, _set: { enabled: false }) {
id enabled
}
}Note:
config_safeexposes field metadata (e.g.{ "apiKey": { "hasSecret": true } }) but never the plaintext secret.