{"info":{"_postman_id":"0a887196-9c41-4c91-987b-983aa882d323","name":"CORALIX API","description":"<html><head></head><body><p>Welcome to the <strong>CORALIX API</strong>.</p>\n<p>Our API allows you to integrate payment processing into your application quickly and securely. Through a simple REST interface, you can create payment sessions, process transactions, manage refunds, tokenize payment methods, and receive real-time transaction updates via webhooks.</p>\n<p>This documentation includes:</p>\n<ul>\n<li>Authentication</li>\n<li>Payment workflow</li>\n<li>API reference</li>\n<li>Request and response examples</li>\n<li>Error codes</li>\n<li>Webhook integration</li>\n<li>Testing guidelines</li>\n<li>Production best practices</li>\n</ul>\n<p>All API communication is performed over <strong>HTTPS</strong>, and all request and response bodies are formatted as <strong>JSON</strong>.</p>\n<p>Whether you are building a custom checkout, integrating a hosted payment page, or automating payment operations, this guide provides everything you need to complete your integration.</p>\n<p>We look forward to helping you build a reliable and secure payment experience.</p>\n</body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[],"owner":"547480","collectionId":"0a887196-9c41-4c91-987b-983aa882d323","publishedId":"2sBXwyGnNB","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"publishDate":"2026-06-29T08:53:48.000Z"},"item":[{"name":"AUTHENTICATION","item":[],"id":"e73419de-30d6-4059-8672-66ec02d790b2","description":"<p>All CORALIX API traffic is authenticated at the HTTP header level. There is <strong>no****session cookie and no OAuth flow</strong> for the core gateway — every request carries<br />its own credentials in the headers and (for server-to-server calls) is<br />cryptographically signed.</p>\n<p>The <strong>Gateway API</strong> (<code>/v1/sessions</code>) is consumed backend-to-backend and is<br />authenticated with an API key plus an HMAC request signature:</p>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Endpoint family</th>\n<th>Consumer</th>\n<th>Auth model</th>\n<th>Header(s)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Gateway API</strong> (<code>/v1/sessions</code>)</td>\n<td>Your backend (server-to-server)</td>\n<td>API key + HMAC request signature</td>\n<td><code>X-API-Key</code>, <code>X-Timestamp</code>, <code>X-Signature</code></td>\n</tr>\n</tbody>\n</table>\n</div><blockquote>\n<p>All requests must be sent over <strong>HTTPS</strong>. Request and response bodies are<br />always <code>application/json</code>. </p>\n</blockquote>\n<hr />\n<h2 id=\"1-gateway-api--signed-requests-server-to-server\">1. Gateway API — signed requests (server-to-server)</h2>\n<p>Every call to the Gateway API must include the three headers below. The<br />signature proves that the caller holds the <strong>secret key</strong> without ever<br />transmitting the secret over the wire, and the timestamp protects against replay<br />attacks.</p>\n<h3 id=\"11-required-headers\">1.1 Required headers</h3>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Header</th>\n<th>Required</th>\n<th>Example</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>X-API-Key</code></td>\n<td>Yes</td>\n<td><code>pk_test_fecc9278094f9fa2b24197fed9d6647f</code></td>\n<td>Your <strong>public</strong> API key. Identifies the merchant / sub-merchant. Safe to expose. Prefix <code>pk_test_</code> in sandbox, <code>pk_live_</code> in production.</td>\n</tr>\n<tr>\n<td><code>X-Timestamp</code></td>\n<td>Yes</td>\n<td><code>1753081200</code></td>\n<td>Unix time <strong>in seconds</strong> (UTC) at the moment the request is signed. Included in the signature and validated server-side for freshness.</td>\n</tr>\n<tr>\n<td><code>X-Signature</code></td>\n<td>Yes</td>\n<td><code>9f86d081884c7d65…</code></td>\n<td>Lowercase hex <strong>HMAC-SHA256</strong> of the canonical string (see §1.3), keyed with your <strong>secret</strong> key.</td>\n</tr>\n<tr>\n<td><code>Content-Type</code></td>\n<td>Yes (for requests with a body)</td>\n<td><code>application/json</code></td>\n<td>All bodies are JSON.</td>\n</tr>\n<tr>\n<td><code>Idempotency-Key</code></td>\n<td>Optional</td>\n<td><code>test-idem-001</code></td>\n<td>Client-generated unique key. If two requests arrive with the same key, the second returns the stored response instead of executing again. Cached for <strong>24 h</strong>. Recommended on all <code>POST</code> calls.</td>\n</tr>\n</tbody>\n</table>\n</div><h3 id=\"12-the-two-keys\">1.2 The two keys</h3>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Key</th>\n<th>Prefix</th>\n<th>Where it goes</th>\n<th>Rule</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Public key</strong></td>\n<td><code>pk_test_…</code> / <code>pk_live_…</code></td>\n<td>Sent in the clear as <code>X-API-Key</code></td>\n<td>Identifies the merchant. Not secret.</td>\n</tr>\n<tr>\n<td><strong>Secret key</strong></td>\n<td><code>sk_test_…</code> / <code>sk_live_…</code></td>\n<td><strong>Never sent.</strong> Used only as the HMAC key to compute <code>X-Signature</code></td>\n<td>Store server-side only. Treat like a password.</td>\n</tr>\n</tbody>\n</table>\n</div><p>Keys are issued as a pair (the secret is shown only once, at creation). Rotate<br />or revoke a compromised pair from the merchant dashboard.</p>\n<h3 id=\"13-how-x-signature-is-built\">1.3 How <code>X-Signature</code> is built</h3>\n<p>The signature is the HMAC-SHA256 of a <strong>canonical string</strong> made of four lines<br />joined by newline (<code>\\n</code>) characters:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code>METHOD            ← uppercase HTTP verb, e.g. POST\nPATH              ← request path including /v1, WITHOUT the query string\nTIMESTAMP         ← the same value sent in X-Timestamp (unix seconds)\nSHA256(body)      ← lowercase hex SHA-256 of the exact raw request body\n\n</code></pre><p>Concretely:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code>canonical = METHOD + \"\\n\" + PATH + \"\\n\" + TIMESTAMP + \"\\n\" + HEX(SHA256(body))\nsignature = HEX( HMAC_SHA256( canonical, secret_key ) )\n\n</code></pre><p>Rules that must match <strong>exactly</strong>, or the server will return <code>401</code>:</p>\n<ul>\n<li><strong>METHOD</strong> is uppercase (<code>POST</code>, <code>GET</code>, <code>DELETE</code>, …).</li>\n<li><strong>PATH</strong> starts with <code>/</code> and includes the <code>/v1</code> prefix<br />  (e.g. <code>/v1/sessions/3b1211a8-7316-44a9-9764-1cc9e2ab1ca3/pay</code>).<br />  It <strong>excludes</strong> the scheme, host, and any <code>?query=string</code>.</li>\n<li><strong>TIMESTAMP</strong> is identical to the value placed in the <code>X-Timestamp</code> header.</li>\n<li><strong>body</strong> is hashed <strong>byte-for-byte</strong> as it is sent on the wire. Do not<br />  re-serialize, re-order keys, or change whitespace after signing.</li>\n<li>For requests <strong>without a body</strong> (<code>GET</code>, <code>DELETE</code>), hash the <strong>empty string</strong>.<br />  The hash of an empty body is the well-known constant:<br />  <code>e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855</code>.</li>\n<li>Both <code>SHA256(body)</code> and the final HMAC are encoded as <strong>lowercase hex</strong>.</li>\n</ul>\n<h4 id=\"worked-example-canonical-string\">Worked example (canonical string)</h4>\n<p>For <code>POST https://api.coralix.tech/v1/sessions</code> with the body</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\"order_id\":\"order_pm_2\",\"amount\":15.20,\"currency\":\"USD\"}\n\n</code></pre>\n<p>signed at <code>1753081200</code>, the canonical string is:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code>POST\n/v1/sessions\n1753081200\n&lt;sha256-hex-of-the-exact-body-above&gt;\n\n</code></pre><p>and <code>X-Signature = HMAC_SHA256(canonical, sk_live_…)</code> in lowercase hex.</p>\n<h3 id=\"14-x-timestamp-and-replay-protection\">1.4 <code>X-Timestamp</code> and replay protection</h3>\n<p><code>X-Timestamp</code> binds each request to a moment in time. The server rejects a<br />request whose timestamp is too far from server time (outside the allowed<br />clock-skew window), which prevents an intercepted request from being replayed<br />later.</p>\n<ul>\n<li>Send <strong>Unix seconds</strong>, not milliseconds (<code>Math.floor(Date.now()/1000)</code>).</li>\n<li>Keep the signing host's clock in sync (NTP). Excessive drift causes <code>401</code>.</li>\n<li>Reuse the <em>same</em> timestamp value in both the header and the canonical string.</li>\n</ul>\n<blockquote>\n<p>⚠️ <strong>To document per the backend:</strong> the exact allowed clock-skew window (the<br />maximum seconds a request may be old/future before it is rejected). A common<br />default is <strong>±300 s</strong>. Confirm the real value with the gateway team and state<br />it here. </p>\n</blockquote>\n<h3 id=\"15-reference-implementation-nodejs--postman-pre-request-script\">1.5 Reference implementation (Node.js / Postman pre-request script)</h3>\n<p>This is the exact signer used in the CORALIX Postman collection. Drop it into<br />the <strong>Pre-request Script</strong> tab so it runs for every gateway request and injects<br />the three headers automatically.</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">const publicKey = pm.variables.get('coralix_public_key');\nconst secretKey = pm.variables.get('coralix_secret_key');\nif (!publicKey || !secretKey) {\n  throw new Error('Set the coralix_public_key and coralix_secret_key variables');\n}\n// METHOD (uppercase)\nconst method = pm.request.method.toUpperCase();\n// PATH including /v1, WITHOUT query string; resolve any {{vars}} in the path\nconst path = pm.variables.replaceIn('/' + pm.request.url.path.join('/'));\n// Exact body Postman will send (resolve {{vars}}); empty string for GET/DELETE\nconst rawBody = pm.request.body &amp;&amp; pm.request.body.raw ? pm.request.body.raw : '';\nconst body = rawBody ? pm.variables.replaceIn(rawBody) : '';\nconst timestamp = Math.floor(Date.now() / 1000).toString();\nconst bodyHash  = CryptoJS.SHA256(body).toString(CryptoJS.enc.Hex);\nconst canonical = [method, path, timestamp, bodyHash].join('\\n');\nconst signature = CryptoJS.HmacSHA256(canonical, secretKey).toString(CryptoJS.enc.Hex);\npm.request.headers.upsert({ key: 'X-API-Key',   value: publicKey });\npm.request.headers.upsert({ key: 'X-Timestamp', value: timestamp });\npm.request.headers.upsert({ key: 'X-Signature', value: signature });\n\n</code></pre>\n<p>Equivalent server-side (plain Node.js, no Postman):</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-javascript\">const crypto = require('crypto');\nfunction signCoralixRequest({ method, path, body = '', secretKey }) {\n  const timestamp = Math.floor(Date.now() / 1000).toString();\n  const bodyHash  = crypto.createHash('sha256').update(body, 'utf8').digest('hex');\n  const canonical = [method.toUpperCase(), path, timestamp, bodyHash].join('\\n');\n  const signature = crypto.createHmac('sha256', secretKey).update(canonical).digest('hex');\n  return { 'X-Timestamp': timestamp, 'X-Signature': signature };\n}\n\n</code></pre>\n<h3 id=\"16-full-request-example\">1.6 Full request example</h3>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-http\">POST /v1/sessions HTTP/1.1\nHost: api.coralix.tech\nContent-Type: application/json\nIdempotency-Key: order_pm_2\nX-API-Key: pk_live_fecc9278094f9fa2b24197fed9d6647f\nX-Timestamp: 1753081200\nX-Signature: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\n{\n  \"order_id\": \"order_pm_2\",\n  \"amount\": 15.20,\n  \"currency\": \"USD\",\n  \"customer_id\": \"cust_abc123\",\n  \"success_url\": \"https://yoursite.com/success\",\n  \"failure_url\": \"https://yoursite.com/failure\",\n  \"cancel_url\": \"https://yoursite.com/cancel\",\n  \"metadata\": { \"plan\": \"premium\", \"user_tier\": \"vip\" }\n}\n\n</code></pre>\n<hr />\n<h2 id=\"2-legacy--simplified-bearer-key-sandbox-only\">2. Legacy / simplified Bearer key (sandbox only)</h2>\n<p>Some older examples and the sandbox authenticate the Gateway with a single<br />header instead of the HMAC triple:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-http\">Authorization: Bearer &lt;public_key&gt;:&lt;secret_key&gt;\n\n</code></pre>\n<p>The server splits the value on <code>:</code>, looks the merchant up by public key, and<br />verifies the secret against its stored SHA-256 hash.</p>\n<blockquote>\n<p>⚠️ This form transmits the <strong>secret key in plaintext</strong> on every request and<br />offers <strong>no replay protection</strong>. Prefer the signed <code>X-API-Key</code> /<br /><code>X-Timestamp</code> / <code>X-Signature</code> scheme (§1) for anything beyond local testing,<br />and confirm with the gateway team whether the Bearer form is still accepted in<br /><code>live</code>. </p>\n</blockquote>\n<hr />\n<h2 id=\"3-error-responses\">3. Error responses</h2>\n<p>When header authentication fails, the API responds with <code>401 Unauthorized</code> and a<br />JSON body that always carries an <code>error_code</code> and a <code>request_id</code> (quote the<br /><code>request_id</code> when contacting support):</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n  \"error_code\": \"invalid_signature\",\n  \"message\": \"Signature verification failed\",\n  \"request_id\": \"req_a1b2c3d4\"\n}\n\n</code></pre>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Situation</th>\n<th>Typical <code>error_code</code></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Missing or unknown <code>X-API-Key</code></td>\n<td><code>invalid_api_key</code></td>\n</tr>\n<tr>\n<td><code>X-Signature</code> does not match the recomputed value</td>\n<td><code>invalid_signature</code></td>\n</tr>\n<tr>\n<td><code>X-Timestamp</code> outside the allowed skew window</td>\n<td><code>expired_timestamp</code> / <code>stale_request</code></td>\n</tr>\n</tbody>\n</table>\n</div><blockquote>\n<p>⚠️ <strong>To document per the backend:</strong> the canonical list and exact spelling of<br /><code>error_code</code> values. The table above lists the expected cases — align the<br />strings with what the gateway actually returns. </p>\n</blockquote>\n<hr />\n<h2 id=\"header-quick-reference\">Header quick reference</h2>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Header</th>\n<th>Gateway (signed)</th>\n<th>Gateway (legacy Bearer)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>X-API-Key</code></td>\n<td>✅</td>\n<td>—</td>\n</tr>\n<tr>\n<td><code>X-Timestamp</code></td>\n<td>✅</td>\n<td>—</td>\n</tr>\n<tr>\n<td><code>X-Signature</code></td>\n<td>✅</td>\n<td>—</td>\n</tr>\n<tr>\n<td><code>Authorization: Bearer pk:sk</code></td>\n<td>—</td>\n<td>✅</td>\n</tr>\n<tr>\n<td><code>Content-Type: application/json</code></td>\n<td>✅</td>\n<td>✅</td>\n</tr>\n<tr>\n<td><code>Idempotency-Key</code></td>\n<td>optional</td>\n<td>optional</td>\n</tr>\n</tbody>\n</table>\n</div>","_postman_id":"e73419de-30d6-4059-8672-66ec02d790b2"},{"name":"PAYMENT FLOW","item":[],"id":"dee212b1-bb84-4779-8e35-50f05201d8eb","description":"<p><strong>1. Create a payment session</strong> — <code>POST /v1/sessions</code></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{ \"order_id\": \"order_123\", \"amount\": 15.00, \"currency\": \"USD\" }\n\n</code></pre>\n<p>Returns the session (<code>status: \"pending\"</code>), a <code>checkout_url</code>, and the available <code>payment_methods</code>. Sessions expire after 30 minutes.</p>\n<p><strong>2. Process the payment</strong> — <code>POST /v1/sessions/{sessionId}/pay</code></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n  \"payment_method_config_id\": \"&lt;id from payment_methods&gt;\",\n  \"payment_data\": {\n    \"card_number\": \"4242424242424242\",\n    \"exp_month\": 12, \"exp_year\": 2030,\n    \"cvv\": \"123\", \"holder_name\": \"John Doe\"\n  }\n}\n\n</code></pre>\n<p>Returns <code>{ status: \"success\", transaction_id, provider_ref }</code>, or on a decline <code>{ status: \"failed\", decline_reason, decline_message }</code>. Use <code>Accept-Language: es</code> or <code>en</code> to choose the message language.</p>\n<p><strong>3. Receive the webhook</strong> — Coralix <code>POST</code>s a signed <code>payment.success</code> / <code>payment.failed</code> event to your configured endpoint. You don't call this; it's pushed.</p>\n<h3 id=\"optional\">Optional</h3>\n<ul>\n<li><code>GET /v1/sessions/{sessionId}/status</code> — check a session and its transactions.</li>\n<li><code>GET /v1/transactions/{id}/attempts</code> — see each attempt and its decline reason.</li>\n</ul>\n<h2 id=\"idempotency\">Idempotency</h2>\n<p><code>order_id</code> is unique per merchant. Retrying with the <strong>same</strong> <code>order_id</code> + <code>amount</code> + <code>currency</code> returns the same session (safe retries, no double charge). Reusing an <code>order_id</code> with a different amount or currency returns <code>409</code>.</p>\n<h2 id=\"errors\">Errors</h2>\n<p>Errors return <code>{ \"error_code\", \"message\", \"request_id\" }</code>. Declines are <strong>not</strong> errors — <code>/pay</code> returns <code>200</code> with <code>status: \"failed\"</code> and a <code>decline_message</code>.</p>\n","_postman_id":"dee212b1-bb84-4779-8e35-50f05201d8eb"},{"name":"ENDPOINTS","item":[{"name":"1. CREATE PAYMENT SESSION","event":[{"listen":"prerequest","script":{"id":"0d98a74a-f277-4bfa-b3c6-2e64377393c8","exec":["/*"," * Coralix gateway — HMAC request signing (Postman Pre-request Script)"," * ------------------------------------------------------------------"," * Paste this into the \"Pre-request Script\" tab of your Collection (so it runs"," * for every gateway request) or a single request."," *"," * Define these variables (Collection/Environment — keep the secret as type \"secret\"):"," *   coralix_public_key   e.g. pk_test_bet365"," *   coralix_secret_key   e.g. sk_test_bet365_secret"," *"," * It signs METHOD\\nPATH\\nTIMESTAMP\\nSHA256(body) and injects X-API-Key,"," * X-Timestamp and X-Signature automatically — no manual header rows needed."," */","//const CryptoJS = require('crypto-js');","","const publicKey = pm.variables.get('coralix_public_key');","const secretKey = pm.variables.get('coralix_secret_key');","if (!publicKey || !secretKey) {","  throw new Error('Set the coralix_public_key and coralix_secret_key variables');","}","","// METHOD (uppercase)","const method = pm.request.method.toUpperCase();","","// PATH including /v1, WITHOUT query string; resolve any {{vars}} in the path","const path = pm.variables.replaceIn('/' + pm.request.url.path.join('/'));","","// Exact body Postman will send (resolve {{vars}}); empty string for GET/DELETE","const rawBody = pm.request.body && pm.request.body.raw ? pm.request.body.raw : '';","const body = rawBody ? pm.variables.replaceIn(rawBody) : '';","","const timestamp = Math.floor(Date.now() / 1000).toString();","const bodyHash = CryptoJS.SHA256(body).toString(CryptoJS.enc.Hex);","const canonical = [method, path, timestamp, bodyHash].join('\\n');","const signature = CryptoJS.HmacSHA256(canonical, secretKey).toString(CryptoJS.enc.Hex);","","pm.request.headers.upsert({ key: 'X-API-Key', value: publicKey });","pm.request.headers.upsert({ key: 'X-Timestamp', value: timestamp });","pm.request.headers.upsert({ key: 'X-Signature', value: signature });","","// Handy while debugging (open the Postman Console):","console.log('[coralix] canonical:\\n' + canonical);","console.log('[coralix] X-Signature: ' + signature);",""],"type":"text/javascript","packages":{}}}],"id":"6cd72966-2f76-4504-8f81-45411c127079","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"noauth","isInherited":false},"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n    \"order_id\": \"merchx_order_00001\",\n    \"amount\": 15.20,\n    \"currency\": \"USD\",\n    \"customer_id\": \"cust_abc123\",\n    \"success_url\": \"https://yoursite.com/success\",\n    \"failure_url\": \"https://yoursite.com/failure\",\n    \"cancel_url\": \"https://yoursite.com/cancel\",\n    \"metadata\": {\n        \"plan\": \"premium\",\n        \"user_tier\": \"vip\"\n    }\n}","options":{"raw":{"language":"json"}}},"url":"https://api.coralix.tech/v1/sessions","urlObject":{"protocol":"https","path":["v1","sessions"],"host":["api","coralix","tech"],"query":[],"variable":[]}},"response":[{"id":"637f12a3-e77a-4042-8c07-b4f165e3110a","name":"1. CREATE PAYMENT SESSION - 201","originalRequest":{"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n    \"order_id\": \"merchx_order_00001\",\n    \"amount\": 15.20,\n    \"currency\": \"USD\",\n    \"customer_id\": \"cust_abc123\",\n    \"success_url\": \"https://yoursite.com/success\",\n    \"failure_url\": \"https://yoursite.com/failure\",\n    \"cancel_url\": \"https://yoursite.com/cancel\",\n    \"metadata\": {\n        \"plan\": \"premium\",\n        \"user_tier\": \"vip\"\n    }\n}","options":{"raw":{"language":"json"}}},"url":"https://api.coralix.tech/v1/sessions"},"status":"Created","code":201,"_postman_previewlanguage":"json","header":[{"key":"Date","value":"Mon, 13 Jul 2026 18:18:01 GMT"},{"key":"Content-Type","value":"application/json; charset=utf-8"},{"key":"Content-Length","value":"688"},{"key":"Connection","value":"keep-alive"},{"key":"x-powered-by","value":"Express"},{"key":"vary","value":"Origin"},{"key":"x-ratelimit-limit","value":"100"},{"key":"x-ratelimit-remaining","value":"99"},{"key":"x-ratelimit-reset","value":"60"},{"key":"x-trace-id","value":"4973968a-ec1b-45ba-9e7d-94bc7986bd90"},{"key":"etag","value":"W/\"2b0-HDEAqfmEITCkKSwq2X/XDsVKiRc\""},{"key":"Server","value":"cloudflare"},{"key":"x-do-app-origin","value":"c322b499-09e8-4d15-94e7-8d66106abf95"},{"key":"Cache-Control","value":"private"},{"key":"x-do-orig-status","value":"201"},{"key":"cf-cache-status","value":"DYNAMIC"},{"key":"set-cookie","value":"__cf_bm=9ER39WbIDfLB6RodOD.c0U0QAdJZZVzV05dCM4MdAS0-1783966681.3679717-1.0.1.1-ajZAeMf1QLSjpYoUB68CsmbiurW2dI.usJxJJXL1K_kmZcz6uPhR3iDl0fgTFLO5OiPxFyG8Hzr5ERCUIXiaEDbCHen_tw.MDT.jKjCp21AoxOBDAS5GGfzJQsZEOekg; HttpOnly; SameSite=None; Secure; Path=/; Domain=api.coralix.tech; Expires=Mon, 13 Jul 2026 18:48:01 GMT"},{"key":"CF-RAY","value":"a1aa49ae8e3bc646-SJO"},{"key":"alt-svc","value":"h3=\":443\"; ma=86400"}],"cookie":[],"responseTime":null,"body":"{\n    \"id\": \"44b45c13-8b0e-4697-af40-e1ae83d8c1f9\",\n    \"merchant_id\": \"25e2cf6c-21bd-4e5e-bb7d-7f445fe80cbf\",\n    \"order_id\": \"merchx_order_00001\",\n    \"amount\": \"15.20\",\n    \"currency\": \"USD\",\n    \"status\": \"success\",\n    \"customer_id\": \"cust_abc123\",\n    \"success_url\": \"https://yoursite.com/success\",\n    \"failure_url\": \"https://yoursite.com/failure\",\n    \"cancel_url\": \"https://yoursite.com/cancel\",\n    \"metadata\": {\n        \"plan\": \"premium\",\n        \"user_tier\": \"vip\"\n    },\n    \"expires_at\": \"2026-07-13T18:23:59.200Z\",\n    \"created_at\": \"2026-07-13T17:53:59.204Z\",\n    \"checkout_url\": \"http://localhost:3000/checkout/44b45c13-8b0e-4697-af40-e1ae83d8c1f9\",\n    \"payment_methods\": [\n        {\n            \"id\": \"c0658ab5-0592-49e4-a6c4-b469b88091c3\",\n            \"display_name\": \"Tarjeta (MockCard)\",\n            \"payment_method_type\": \"card\"\n        }\n    ]\n}"},{"id":"65b232cb-b903-45d1-8d9a-2b3d13d596a2","name":"1. CREATE PAYMENT SESSION - 401","originalRequest":{"method":"POST","header":[],"body":{"mode":"raw","raw":"{\n    \"order_id\": \"merchx_order_00002\",\n    \"amount\": 15.20,\n    \"currency\": \"USD\",\n    \"customer_id\": \"cust_abc123\",\n    \"success_url\": \"https://yoursite.com/success\",\n    \"failure_url\": \"https://yoursite.com/failure\",\n    \"cancel_url\": \"https://yoursite.com/cancel\",\n    \"metadata\": {\n        \"plan\": \"premium\",\n        \"user_tier\": \"vip\"\n    }\n}","options":{"raw":{"language":"json"}}},"url":"https://api.coralix.tech/v1/sessions"},"status":"Unauthorized","code":401,"_postman_previewlanguage":"json","header":[{"key":"Date","value":"Mon, 13 Jul 2026 18:20:40 GMT"},{"key":"Content-Type","value":"application/json; charset=utf-8"},{"key":"Content-Length","value":"126"},{"key":"Connection","value":"keep-alive"},{"key":"x-powered-by","value":"Express"},{"key":"vary","value":"Origin"},{"key":"x-ratelimit-limit","value":"100"},{"key":"x-ratelimit-remaining","value":"98"},{"key":"x-ratelimit-reset","value":"33"},{"key":"etag","value":"W/\"7e-Gy6ZYenT4Ac+RqTLTJ1Xa1y+Abc\""},{"key":"Server","value":"cloudflare"},{"key":"x-do-app-origin","value":"c322b499-09e8-4d15-94e7-8d66106abf95"},{"key":"Cache-Control","value":"private"},{"key":"x-do-orig-status","value":"401"},{"key":"cf-cache-status","value":"DYNAMIC"},{"key":"CF-RAY","value":"a1aa4d8fb81fc646-SJO"},{"key":"alt-svc","value":"h3=\":443\"; ma=86400"}],"cookie":[],"responseTime":null,"body":"{\n    \"error_code\": \"invalid_authentication\",\n    \"message\": \"Invalid authentication\",\n    \"request_id\": \"b31ef3b8-5445-4046-abc1-3292389d6867\"\n}"}],"_postman_id":"6cd72966-2f76-4504-8f81-45411c127079"},{"name":"2. PAY  PAYMENT SESSION","event":[{"listen":"prerequest","script":{"id":"ab43e383-fe92-40b6-a314-25db2cfc240c","exec":["/*"," * Coralix gateway — HMAC request signing (Postman Pre-request Script)"," * ------------------------------------------------------------------"," * Paste this into the \"Pre-request Script\" tab of your Collection (so it runs"," * for every gateway request) or a single request."," *"," * Define these variables (Collection/Environment — keep the secret as type \"secret\"):"," *   coralix_public_key   e.g. pk_test_bet365"," *   coralix_secret_key   e.g. sk_test_bet365_secret"," *"," * It signs METHOD\\nPATH\\nTIMESTAMP\\nSHA256(body) and injects X-API-Key,"," * X-Timestamp and X-Signature automatically — no manual header rows needed."," */","//const CryptoJS = require('crypto-js');","","const publicKey = pm.variables.get('coralix_public_key');","const secretKey = pm.variables.get('coralix_secret_key');","if (!publicKey || !secretKey) {","  throw new Error('Set the coralix_public_key and coralix_secret_key variables');","}","","// METHOD (uppercase)","const method = pm.request.method.toUpperCase();","","// PATH including /v1, WITHOUT query string; resolve any {{vars}} in the path","const path = pm.variables.replaceIn('/' + pm.request.url.path.join('/'));","","// Exact body Postman will send (resolve {{vars}}); empty string for GET/DELETE","const rawBody = pm.request.body && pm.request.body.raw ? pm.request.body.raw : '';","const body = rawBody ? pm.variables.replaceIn(rawBody) : '';","","const timestamp = Math.floor(Date.now() / 1000).toString();","const bodyHash = CryptoJS.SHA256(body).toString(CryptoJS.enc.Hex);","const canonical = [method, path, timestamp, bodyHash].join('\\n');","const signature = CryptoJS.HmacSHA256(canonical, secretKey).toString(CryptoJS.enc.Hex);","","pm.request.headers.upsert({ key: 'X-API-Key', value: publicKey });","pm.request.headers.upsert({ key: 'X-Timestamp', value: timestamp });","pm.request.headers.upsert({ key: 'X-Signature', value: signature });","","// Handy while debugging (open the Postman Console):","console.log('[coralix] canonical:\\n' + canonical);","console.log('[coralix] X-Signature: ' + signature);",""],"type":"text/javascript","packages":{}}}],"id":"6b64dbef-981b-43bf-a968-1e99162eead8","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer pk_test_bet365:sk_test_bet365_secret","type":"text"}],"body":{"mode":"raw","raw":"{\n    \"payment_method_config_id\": \"c0658ab5-0592-49e4-a6c4-b469b88091c3\",\n    \"payment_data\": {\n      \"card_number\": \"4242424242424242\",\n      \"exp_month\": 12,\n      \"exp_year\": 2030,\n      \"cvv\": \"123\",\n      \"holder_name\": \"John Doe\"\n    }\n  }","options":{"raw":{"language":"json"}}},"url":"https://api.coralix.tech/v1/sessions/44b45c13-8b0e-4697-af40-e1ae83d8c1f9/pay","urlObject":{"protocol":"https","path":["v1","sessions","44b45c13-8b0e-4697-af40-e1ae83d8c1f9","pay"],"host":["api","coralix","tech"],"query":[],"variable":[]}},"response":[{"id":"0b98c517-fa5e-4f28-8587-5d865086daf4","name":"2. PAY  PAYMENT SESSION - 200","originalRequest":{"method":"POST","header":[{"key":"Authorization","value":"Bearer pk_test_bet365:sk_test_bet365_secret","type":"text"}],"body":{"mode":"raw","raw":"{\n    \"payment_method_config_id\": \"c0658ab5-0592-49e4-a6c4-b469b88091c3\",\n    \"payment_data\": {\n      \"card_number\": \"4242424242424242\",\n      \"exp_month\": 12,\n      \"exp_year\": 2030,\n      \"cvv\": \"123\",\n      \"holder_name\": \"John Doe\"\n    }\n  }","options":{"raw":{"language":"json"}}},"url":"https://api.coralix.tech/v1/sessions/ec00c38e-e3d7-4943-9ecd-172835c333bf/pay"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Date","value":"Mon, 13 Jul 2026 18:31:14 GMT"},{"key":"Content-Type","value":"application/json; charset=utf-8"},{"key":"Transfer-Encoding","value":"chunked"},{"key":"Connection","value":"keep-alive"},{"key":"x-powered-by","value":"Express"},{"key":"vary","value":"Origin"},{"key":"x-ratelimit-limit","value":"100"},{"key":"x-ratelimit-remaining","value":"99"},{"key":"x-ratelimit-reset","value":"60"},{"key":"x-trace-id","value":"83e63750-dff0-4281-ab73-47cfc029214f"},{"key":"etag","value":"W/\"b1-hsGqSNqcDBBJP3JG75MR04cSduk\""},{"key":"Server","value":"cloudflare"},{"key":"x-do-app-origin","value":"c322b499-09e8-4d15-94e7-8d66106abf95"},{"key":"Cache-Control","value":"private"},{"key":"x-do-orig-status","value":"200"},{"key":"cf-cache-status","value":"DYNAMIC"},{"key":"Content-Encoding","value":"br"},{"key":"CF-RAY","value":"a1aa5d089e17eb6f-SJO"},{"key":"alt-svc","value":"h3=\":443\"; ma=86400"}],"cookie":[],"responseTime":null,"body":"{\n    \"session_id\": \"ec00c38e-e3d7-4943-9ecd-172835c333bf\",\n    \"transaction_id\": \"b6eee137-2b06-4054-be27-3d81a1944249\",\n    \"status\": \"success\",\n    \"provider_ref\": \"MOCKCARD-1783967474304-ec00c38e\"\n}"}],"_postman_id":"6b64dbef-981b-43bf-a968-1e99162eead8"}],"id":"298c6e0f-3b58-490b-a923-225dcc17b0c5","_postman_id":"298c6e0f-3b58-490b-a923-225dcc17b0c5","description":""},{"name":"TESTING","item":[],"id":"f806fbc5-df9f-42ca-92c5-4ae16ca9ed13","description":"<h2 id=\"testing\">Testing</h2>\n<p>Use these test cards as <code>payment_data.card_number</code> in <code>POST /v1/sessions/{sessionId}/pay</code>. Any card needs a <strong>future expiry</strong>, a <strong>3-digit CVV</strong>, and a <strong>non-empty holder name</strong>.</p>\n<h3 id=\"approved-card\">Approved card</h3>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Card number</th>\n<th>Result</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>4242 4242 4242 4242</code></td>\n<td>✅ <code>status: \"success\"</code></td>\n</tr>\n</tbody>\n</table>\n</div><h3 id=\"decline-cards\">Decline cards</h3>\n<p>Each card forces a specific decline. The response is <code>200</code> with <code>status: \"failed\"</code>, a <code>decline_reason</code> and a localized <code>decline_message</code>.</p>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Card number</th>\n<th><code>decline_reason</code></th>\n<th><code>decline_message</code> (en)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>4000 0000 0000 9995</code></td>\n<td><code>insufficient_funds</code></td>\n<td>Insufficient funds. Please try with another card.</td>\n</tr>\n<tr>\n<td><code>4000 0000 0000 0002</code></td>\n<td><code>do_not_honor</code></td>\n<td>Your bank declined the payment. Please contact your bank.</td>\n</tr>\n<tr>\n<td><code>4000 0000 0000 0069</code></td>\n<td><code>expired_card</code></td>\n<td>Your card has expired. Please use a valid card.</td>\n</tr>\n<tr>\n<td><code>4000 0000 0000 0127</code></td>\n<td><code>incorrect_cvv</code></td>\n<td>Incorrect CVV. Please check your security code.</td>\n</tr>\n<tr>\n<td><code>4100 0000 0000 0019</code></td>\n<td><code>fraud_suspected</code></td>\n<td>Transaction declined for security reasons.</td>\n</tr>\n<tr>\n<td><code>4000 0000 0000 4854</code></td>\n<td><code>lost_card</code></td>\n<td>Card reported as lost.</td>\n</tr>\n</tbody>\n</table>\n</div><p>Any other <strong>well-formed</strong> card number that isn't <code>4242…</code> is declined as <code>do_not_honor</code>.</p>\n<h3 id=\"input-validation\">Input validation</h3>\n<p>Malformed card data is rejected (also <code>status: \"failed\"</code>):</p>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Condition</th>\n<th><code>decline_reason</code></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Card number not 13–19 digits, or missing holder name</td>\n<td><code>invalid_card</code></td>\n</tr>\n<tr>\n<td>CVV not exactly 3 digits</td>\n<td><code>incorrect_cvv</code></td>\n</tr>\n<tr>\n<td>Invalid month / non-integer year / past expiry</td>\n<td><code>expired_card</code></td>\n</tr>\n</tbody>\n</table>\n</div><h3 id=\"example--approved-payment\">Example — approved payment</h3>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">POST /v1/sessions/{sessionId}/pay\n{\n  \"payment_method_config_id\": \"&lt;id from the session&amp;#x27;s payment_methods&gt;\",\n  \"payment_data\": {\n    \"card_number\": \"4242424242424242\",\n    \"exp_month\": 12,\n    \"exp_year\": 2030,\n    \"cvv\": \"123\",\n    \"holder_name\": \"John Doe\"\n  }\n}\n\n</code></pre>\n<p>Send <code>Accept-Language: es</code> to receive Spanish <code>decline_message</code>s.</p>\n<blockquote>\n<p>These cards are sandbox-only. Production requires a real PSP adapter.</p>\n</blockquote>\n","_postman_id":"f806fbc5-df9f-42ca-92c5-4ae16ca9ed13"},{"name":"DECLINE REASONS REFERENCE","item":[],"id":"ab621510-1780-476f-95ad-66f27b31afbe","description":"<p>When a payment is declined, <code>POST /v1/sessions/{sessionId}/pay</code> returns <strong><code>200</code></strong> with <code>status: \"failed\"</code>, a machine-readable <strong><code>decline_reason</code></strong> and a customer-facing <strong><code>decline_message</code></strong>. The same <code>decline_reason</code> / <code>decline_message</code> appear on <code>GET /v1/transactions/{id}/attempts</code> and inside the <code>payment.failed</code> webhook payload.</p>\n<p>Messages are returned in English by default; send <code>Accept-Language: es</code> for Spanish.</p>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th><code>decline_reason</code></th>\n<th>Category</th>\n<th><code>decline_message</code></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>insufficient_funds</code></td>\n<td>card</td>\n<td>Insufficient funds. Please try with another card.</td>\n</tr>\n<tr>\n<td><code>invalid_card</code></td>\n<td>card</td>\n<td>Invalid card. Please check your card details and try again.</td>\n</tr>\n<tr>\n<td><code>expired_card</code></td>\n<td>card</td>\n<td>Your card has expired. Please use a valid card.</td>\n</tr>\n<tr>\n<td><code>incorrect_cvv</code></td>\n<td>card</td>\n<td>Incorrect CVV. Please check your security code.</td>\n</tr>\n<tr>\n<td><code>do_not_honor</code></td>\n<td>issuer</td>\n<td>Your bank declined the payment. Please contact your bank.</td>\n</tr>\n<tr>\n<td><code>card_blocked</code></td>\n<td>issuer</td>\n<td>Card blocked. Please contact your bank.</td>\n</tr>\n<tr>\n<td><code>lost_card</code></td>\n<td>issuer</td>\n<td>Card reported as lost.</td>\n</tr>\n<tr>\n<td><code>stolen_card</code></td>\n<td>issuer</td>\n<td>Card reported as stolen.</td>\n</tr>\n<tr>\n<td><code>limit_exceeded</code></td>\n<td>issuer</td>\n<td>Credit limit exceeded.</td>\n</tr>\n<tr>\n<td><code>card_velocity_exceeded</code></td>\n<td>issuer</td>\n<td>Too many recent transactions. Please wait and try again.</td>\n</tr>\n<tr>\n<td><code>invalid_amount</code></td>\n<td>gateway</td>\n<td>Invalid amount.</td>\n</tr>\n<tr>\n<td><code>currency_not_supported</code></td>\n<td>gateway</td>\n<td>Currency not supported by this card.</td>\n</tr>\n<tr>\n<td><code>duplicate_transaction</code></td>\n<td>gateway</td>\n<td>Duplicate transaction.</td>\n</tr>\n<tr>\n<td><code>fraud_suspected</code></td>\n<td>risk</td>\n<td>Transaction declined for security reasons.</td>\n</tr>\n<tr>\n<td><code>3ds_failed</code></td>\n<td>3ds</td>\n<td>Authentication failed. Please try again.</td>\n</tr>\n<tr>\n<td><code>3ds_not_supported</code></td>\n<td>3ds</td>\n<td>Your bank does not support the required authentication.</td>\n</tr>\n<tr>\n<td><code>bank_unreachable</code></td>\n<td>network</td>\n<td>Your bank is currently unavailable. Please try again later.</td>\n</tr>\n<tr>\n<td><code>processing_error</code></td>\n<td>network</td>\n<td>Processing error. Please try again.</td>\n</tr>\n<tr>\n<td><code>country_not_supported</code></td>\n<td>compliance</td>\n<td>Country not supported for this payment method.</td>\n</tr>\n<tr>\n<td><code>card_type_not_supported</code></td>\n<td>compliance</td>\n<td>Card type not accepted.</td>\n</tr>\n<tr>\n<td><code>amount_too_small</code></td>\n<td>limits</td>\n<td>Amount is below the minimum allowed.</td>\n</tr>\n<tr>\n<td><code>unknown_decline</code></td>\n<td>fallback</td>\n<td>Payment not processed. Please contact your bank.</td>\n</tr>\n</tbody>\n</table>\n</div><p><strong>Notes</strong></p>\n<ul>\n<li><p><code>category</code> is informational (groups the reason: <code>card</code>, <code>issuer</code>, <code>gateway</code>, <code>risk</code>, <code>3ds</code>, <code>network</code>, <code>compliance</code>, <code>limits</code>, <code>fallback</code>).</p>\n</li>\n<li><p>Any unrecognized provider code maps to <strong><code>unknown_decline</code></strong> (the fallback), so your integration should always handle that case.</p>\n</li>\n<li><p>Show <code>decline_message</code> to the buyer; branch your logic on the stable <code>decline_reason</code> code, never on the message text.</p>\n</li>\n</ul>\n","_postman_id":"ab621510-1780-476f-95ad-66f27b31afbe"}],"event":[{"listen":"prerequest","script":{"id":"7be3f853-e74a-4edc-9e3d-b1f091241cb9","type":"text/javascript","packages":{},"exec":[""]}},{"listen":"test","script":{"id":"e9e53639-69b5-4e9d-b713-7e34e863026d","type":"text/javascript","packages":{},"exec":[""]}}],"variable":[{"key":"coralix_public_key","value":"pk_test_fecc9278094f9fa2b24197fed9d6647f4","type":"string"},{"key":"coralix_secret_key","value":"sk_test_50cba8238dae1f7211bfbd6d8e165dc85ef96b52b7d17f9f4","type":"string"}]}