Authentication
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
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
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
OAuth tokens (oa_...) act on behalf of multiple biller accounts. Include X-Biller-Account with every request:
curl https://api.elasticpay.co/api/v1/payment_intents \ -H "Authorization: Bearer oa_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "X-Biller-Account: biller_abc123"const response = await fetch('https://api.elasticpay.co/api/v1/payment_intents', { headers: { 'Authorization': 'Bearer oa_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Biller-Account': 'biller_abc123', },});response = requests.get( 'https://api.elasticpay.co/api/v1/payment_intents', headers={ 'Authorization': 'Bearer oa_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Biller-Account': 'biller_abc123', },)$response = $client->get('/api/v1/payment_intents', [ 'headers' => [ 'Authorization' => 'Bearer oa_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Biller-Account' => 'biller_abc123', ],]);response = conn.get('/api/v1/payment_intents') do |req| req.headers['Authorization'] = 'Bearer oa_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' req.headers['X-Biller-Account'] = 'biller_abc123'endhttp.DefaultRequestHeaders.Add("X-Biller-Account", "biller_abc123");// Authorization already set with oa_ tokenvar response = await http.GetAsync("/api/v1/payment_intents");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
- 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.