Authentication
Key types
Section titled “Key types”| Type | Prefix | Use |
|---|---|---|
| Secret key | sk_ |
Server-side only - full API access |
| Publishable key | pk_ |
Client-side - widget initialisation and tokenisation only |
| OAuth token | oa_ |
Platform integrations acting on behalf of a biller account |
Never expose a secret key in browser code, mobile apps, or source control.
Sending a secret key
Section titled “Sending a secret key”Pass your secret key as a Bearer token in the Authorization header:
curl https://api.elasticpay.co/api/v1/payment_intents \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"const response = await fetch('https://api.elasticpay.co/api/v1/payment_intents', { headers: { 'Authorization': 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' },});import requests
response = requests.get( 'https://api.elasticpay.co/api/v1/payment_intents', headers={'Authorization': 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'},)<?phpuse GuzzleHttp\Client;
$client = new Client([ 'base_uri' => 'https://api.elasticpay.co', 'headers' => ['Authorization' => 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'],]);$response = $client->get('/api/v1/payment_intents');require 'faraday'
conn = Faraday.new('https://api.elasticpay.co')conn.headers['Authorization'] = 'Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'response = conn.get('/api/v1/payment_intents')using var http = new HttpClient { BaseAddress = new Uri("https://api.elasticpay.co") };http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");var response = await http.GetAsync("/api/v1/payment_intents");Client secret header
Section titled “Client secret header”When confirming a payment from the client side with a publishable key, include the payment intent’s client_secret in X-Client-Secret:
curl -X POST https://api.elasticpay.co/api/v1/payment_intents/pi_0abc123/confirm \ -H "Authorization: Bearer pk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "X-Client-Secret: pi_0abc123_secret_xyz987" \ -H "Content-Type: application/json" \ -d '{"payment_method": "pm_0xyz789"}'const response = await fetch( 'https://api.elasticpay.co/api/v1/payment_intents/pi_0abc123/confirm', { method: 'POST', headers: { 'Authorization': 'Bearer pk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Client-Secret': 'pi_0abc123_secret_xyz987', 'Content-Type': 'application/json', }, body: JSON.stringify({ payment_method: 'pm_0xyz789' }), });response = requests.post( 'https://api.elasticpay.co/api/v1/payment_intents/pi_0abc123/confirm', headers={ 'Authorization': 'Bearer pk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Client-Secret': 'pi_0abc123_secret_xyz987', }, json={'payment_method': 'pm_0xyz789'},)$response = $client->post('/api/v1/payment_intents/pi_0abc123/confirm', [ 'headers' => [ 'Authorization' => 'Bearer pk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Client-Secret' => 'pi_0abc123_secret_xyz987', ], 'json' => ['payment_method' => 'pm_0xyz789'],]);response = conn.post('/api/v1/payment_intents/pi_0abc123/confirm') do |req| req.headers['Authorization'] = 'Bearer pk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' req.headers['X-Client-Secret'] = 'pi_0abc123_secret_xyz987' req.body = { payment_method: 'pm_0xyz789' }.to_jsonendhttp.DefaultRequestHeaders.Add("X-Client-Secret", "pi_0abc123_secret_xyz987");var response = await http.PostAsJsonAsync( "/api/v1/payment_intents/pi_0abc123/confirm", new { payment_method = "pm_0xyz789" });The widget handles this automatically - you only need this header if calling confirm directly from the browser.
OAuth tokens
Section titled “OAuth tokens”OAuth tokens (oa_...) act on behalf of multiple biller accounts. Include X-Biller-Account with every request. A call to the REST API with an OAuth token must carry X-Biller-Account: acct_… naming the account it acts on (the biller_id that whoami reports); the MCP server sends it for you.
curl https://api.elasticpay.co/api/v1/payment_intents \ -H "Authorization: Bearer oa_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "X-Biller-Account: acct_k8mPq2VtRs"const response = await fetch('https://api.elasticpay.co/api/v1/payment_intents', { headers: { 'Authorization': 'Bearer oa_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Biller-Account': 'acct_k8mPq2VtRs', },});response = requests.get( 'https://api.elasticpay.co/api/v1/payment_intents', headers={ 'Authorization': 'Bearer oa_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Biller-Account': 'acct_k8mPq2VtRs', },)$response = $client->get('/api/v1/payment_intents', [ 'headers' => [ 'Authorization' => 'Bearer oa_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Biller-Account' => 'acct_k8mPq2VtRs', ],]);response = conn.get('/api/v1/payment_intents') do |req| req.headers['Authorization'] = 'Bearer oa_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' req.headers['X-Biller-Account'] = 'acct_k8mPq2VtRs'endhttp.DefaultRequestHeaders.Add("X-Biller-Account", "acct_k8mPq2VtRs");// Authorization already set with oa_ tokenvar response = await http.GetAsync("/api/v1/payment_intents");Key formats
Section titled “Key formats”| Type | Format |
|---|---|
| Secret sandbox | sk_sandbox_[32 alphanumeric] |
| Secret live | sk_live_[32 alphanumeric] |
| Publishable sandbox | pk_sandbox_[32 alphanumeric] |
| Publishable live | pk_live_[32 alphanumeric] |
| OAuth | oa_sandbox_... / oa_live_... |
Keeping keys safe
Section titled “Keeping keys safe”- Use
pk_keys on the client side - they are scoped to tokenisation only and cannot charge or refund. - Never commit keys to source control. Use environment variables or a secrets manager.
- Rotate keys immediately if you suspect exposure. Revocation takes effect instantly.