Skip to content
API v1

Take payments with the Efespay API.

Open a payment session with a single REST API, get the result through a signed webhook, and refund or cancel on the same record. This page gathers what you need for the integration in one place.

The API and test environment are in pre-pilot preparation. Live mode opens once the business application is approved.

Quick start

Your first test payment in three steps: get a key, open a session, verify the result.

API base: https://api.efespay.com/v1

  1. 1. Get a test key

    Create a secret test key in the panel under Developers → API keys. The key starts with efp_test_sk_ and is shown only once; store it as an environment variable on your server.

  2. 2. Open a payment session

    Send the amount in minor units (amount_minor: 125000 = TRY 1,250.00) with your order reference. Redirect the customer to the url in the response.

    Create a payment session
    curl https://api.efespay.com/v1/payment-sessions \
      -H "Authorization: Bearer $EFESPAY_SECRET_KEY" \
      -H "Idempotency-Key: order-1001" \
      -H "Content-Type: application/json" \
      -d '{
        "order_reference": "ORDER-1001",
        "amount_minor": 125000,
        "currency": "TRY",
        "installment_options": [1, 3, 6],
        "return_url": "https://shop.example.com/order/1001"
      }'
  3. 3. Verify the result

    The customer returning to your site is not proof of payment. Before fulfilling the order, read the verified status with GET /v1/payments/{id} or from the webhook.

    Retrieve the payment
    curl https://api.efespay.com/v1/payments/pay_… \
      -H "Authorization: Bearer $EFESPAY_SECRET_KEY"

Authentication

Every request carries an API key in the Authorization header. The business and mode come from the key.

Check the key
curl https://api.efespay.com/v1 \
  -H "Authorization: Bearer $EFESPAY_SECRET_KEY"
API key types
PrefixMeaning
efp_test_sk_…Test mode, secret key: all scopes
efp_live_sk_…Live mode, secret key: all scopes (after application approval)
efp_test_rk_…Test mode, restricted key: selected scopes only
efp_live_rk_…Live mode, restricted key: selected scopes only
  • Keys are used on the server only; never put them in a browser or mobile app.
  • A request cannot specify another business: the business and mode (test | live) come from the key.
  • 600 requests per minute per key; beyond that, 429 rate_limited with a Retry-After header.
  • Every response carries Efespay-Request-Id: req_…; share this ID in support requests.
  • Error messages are returned in Turkish or English according to the Accept-Language header.
Restricted key scopes
ScopeMeaning
payment_sessions:writeCreate payment sessions
payment_sessions:readRead payment sessions
payments:readRead payments, attempts and refunds
payments:refundStart refunds
payments:cancelCancel
payment_links:readRead payment links and QR codes
payment_links:writeCreate and deactivate payment links
provider_connections:readRead connections (without credentials)
webhook_endpoints:readRead webhook endpoints
webhook_endpoints:writeCreate and change webhook endpoints, send test events
refund_requests:writeOpen and track refund requests (MCP); not a refund permission
payments:chargeCharge saved cards without the customer present (Idempotency-Key required)
payment_methods:readView saved cards (no token; last 4 and expiry only)
payment_methods:writeDelete saved cards
subscriptions:readView subscriptions
subscriptions:writeCreate, re-card, pause and cancel subscriptions
sub_merchants:readRead sub-merchants (IBAN and ID numbers masked)
sub_merchants:writeCreate and update sub-merchants (including provider registration)

Payment sessions

A payment session is the request that opens the payment page for the customer. One payment record is created per order.

Payment session fields
FieldRequiredDescription
order_referenceRequiredYour order reference. A second open or successful payment cannot be created with the same reference.
amount_minorRequiredAmount as an integer in minor units (125000 = TRY 1,250.00).
currencyOptionalCurrency; only TRY today (default).
descriptionOptionalDescription shown on the payment page.
installment_optionsOptionalInstalment counts offered, 1–12 (e.g. [1, 3, 6]). The response returns each option's total and fee.
customerOptionalname, email, phone, external_reference. The external reference matches the customer in the panel.
return_urlOptionalWhere the customer returns after payment; must be on the allowed address list.
expires_in_minutesOptionalSession lifetime, 5–10080 minutes (default 60).
metadataOptionalFree-form data with up to 20 keys; returned on the payment and in events.
localeOptionalPayment page language: tr or en.
Response: payment_session (201)
{
  "id": "ps_…",
  "object": "payment_session",
  "status": "open",
  "url": "https://efespay.com/pay/ps_…",
  "payment_id": "pay_…",
  "amount_minor": 125000,
  "installment_options": [
    { "installments": 1, "total_minor": 125000, "fee_minor": 0, "monthly_minor": 125000 },
    { "installments": 3, "total_minor": 129875, "fee_minor": 4875, "monthly_minor": 43292 }
  ],
  "expires_at": "2026-10-25T11:00:00.000Z"
}
  • Add an Idempotency-Key to write requests (≤ 255 characters). The same key and body return the stored response within 24 hours (Idempotent-Replayed: true).
  • The same key with a different body returns 409 idempotency_conflict; while the first request is running, 409 idempotency_in_progress.
  • 5xx responses are not stored; you can retry with the same key.
  • Even with a different key, a second open session cannot be created for the same order (409 order_reference_in_use).

Payment statuses

Payment statuses
StatusDescription
pendingThe session is open; the customer has not started paying yet.
processingThe customer is on the provider page; the result is pending.
uncertainNo final result could be obtained from the provider (e.g. a timeout). It is not a failure; Efespay queries the provider.
failedThe attempt failed. The customer can retry in the same session (if the error code allows).
succeededThe payment was verified with the provider. You can fulfil the order.
cancelledThe payment was cancelled (the session was closed or a successful payment was voided).
expiredThe session expired; no payment was taken.

The browser return (return_url) is not proof of payment on its own. Update the order from the webhook or from the verified status in the GET /v1/payments/{id} response.

Webhook verification

Payment, refund and link events reach your server as signed POST requests. Verify the signature before processing the body.

Request headers
POST https://shop.example.com/webhooks/efespay
Content-Type: application/json
User-Agent: Efespay-Webhooks/1.0
Efespay-Event-Id: evt_…
Efespay-Event-Type: payment.succeeded
Efespay-Signature: t=1792922400,v1=5f2c…
Body
{
  "id": "evt_…",
  "object": "event",
  "type": "payment.succeeded",
  "created_at": "2026-10-25T10:00:00.000Z",
  "mode": "test",
  "data": {
    "object": {
      "id": "pay_…",
      "object": "payment",
      "status": "succeeded",
      "amount_minor": 125000,
      "order_reference": "ORDER-1001"
    }
  }
}

Read the t and v1 values from the Efespay-Signature header. Compute HMAC_SHA256(secret, t + "." + raw_body) as hex and compare it with v1 in constant time. Reject the request if t is older than 5 minutes. Verify the raw body before parsing it.

Signature verification (Node.js)
import crypto from "node:crypto";

export function verifyEfespaySignature(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 || "");
  return fresh && a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Express: app.post("/webhooks/efespay", express.raw({ type: "application/json" }), (req, res) => {
//   if (!verifyEfespaySignature(req.body.toString("utf8"), req.get("Efespay-Signature"), process.env.EFESPAY_WEBHOOK_SECRET)) return res.sendStatus(400);
//   const event = JSON.parse(req.body); // deduplicate by event.id
//   res.sendStatus(200);
// });
  • Your endpoint must return 2xx within 10 seconds; redirects (3xx) are not followed.
  • A failed delivery is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h (8 attempts in total).
  • If there is no successful delivery for 3 days, the endpoint is disabled and the business owner is emailed.
  • The same event can arrive more than once: deduplicate with Efespay-Event-Id, and never roll a status back with an older event.
  • The signing secret (whsec_…) is shown only once, when the endpoint is created and when it is rotated.
Event types
EventWhen
payment.authorizedPre-authorization approved: the amount is held on the card, not captured.
payment.capturedThe authorization was captured (in full or in part).
payment.succeededThe payment was verified with the provider.
payment.failedThe attempt failed.
payment.uncertainThe result is not final; verification is in progress.
payment.cancelledThe payment was cancelled.
payment.expiredThe session expired.
refund.succeededThe refund completed.
refund.failedThe refund failed.
refund.uncertainThe refund result is not final.
payment_link.completedThe payment link reached its usage limit.
refund_request.createdAn AI agent opened a refund request (awaiting approval).
refund_request.approvedRefund request approved; the refund started.
refund_request.rejectedRefund request rejected.
refund_request.expiredRefund request was not decided within 7 days.
payment_method.createdA card was saved with the customer's consent.
payment_method.expiredA saved card expired or its provider token is no longer valid.
payment_method.deletedA saved card was deleted.
subscription.createdA subscription was created.
subscription.past_dueA period charge failed; it will be retried.
subscription.pausedA subscription was paused.
subscription.resumedA subscription was resumed.
subscription.cancelledA subscription was cancelled (by the business or after the last retry).
subscription.endedA subscription completed all its charges.
payment_item.approvedA marketplace item was approved; the seller share is paid out on the provider's schedule.
payment_item.disapprovedA marketplace item was not approved or its approval was withdrawn; the seller share is held.
pingA test event sent from the panel or the API (to that endpoint only).

Error codes

API errors share one format; payment result errors are reported with a separate dictionary.

Error response
{
  "error": {
    "code": "order_reference_in_use",
    "message": "There is an open payment with this order reference.",
    "request_id": "req_…",
    "param": "order_reference"
  }
}
API error codes
HTTPCodeMeaning
400invalid_order_referenceThe order reference is missing or invalid.
400invalid_amountThe amount is invalid (must be a positive integer in minor units).
400unsupported_currencyThe currency is not supported.
400return_url_not_allowedThe return address is not on the allowed address list.
400invalid_metadataThe metadata is invalid (up to 20 keys, string values).
400invalid_idempotency_keyThe Idempotency-Key is invalid.
400invalid_cursorThe starting_after cursor is invalid.
400idempotency_key_requiredThis request requires an Idempotency-Key header.
401authentication_requiredThere is no Authorization header.
401invalid_api_keyThe API key is invalid.
401api_key_revokedThe API key has been revoked.
403insufficient_scopeThe restricted key does not carry the scope for this operation (required_scope).
403merchant_suspendedThe business account is suspended.
403live_mode_not_approvedThe business application for live mode has not been approved yet.
403risk_blockedThe business's risk rule blocked the session.
404payment_not_foundThe payment was not found.
404session_not_foundThe payment session was not found.
404payment_method_not_foundSaved card not found.
404subscription_not_foundSubscription not found.
404api_not_foundThere is no such endpoint.
409order_reference_in_useThere is an open or successful payment with this order reference.
409idempotency_conflictThe same Idempotency-Key was used with a different body.
409idempotency_in_progressThe first request with the same key is still being processed.
409payment_not_refundableThe payment cannot be refunded in its current state.
409refund_exceeds_availableThe refund amount exceeds the refundable amount.
409payment_in_progressThe payment is in progress; no operation is possible until the result is final.
409cancel_not_supportedThe provider does not support cancelling this payment; refund it instead.
409payment_method_inactiveThe saved card can't be used (expired or deleted).
409payment_method_in_useThe card is used by an active subscription; change the subscription's card first.
429rate_limitedToo many requests; wait for the Retry-After period.
500internal_errorAn unexpected error. Open a support request with the request_id.

Payment result error codes

On a failed payment, failure.code is one of the codes below; the provider's raw code is never shown to the customer and appears in the transaction detail in the panel. Technical codes are provider-side and counted separately from card declines.

Payment result error codes
CodeMeaningRetryableTechnical
card_declinedThe card's bank declined the transaction.YesNo
insufficient_fundsThe card limit or balance is insufficient.YesNo
expired_cardThe card has expired.YesNo
invalid_cardThe card details are invalid.YesNo
incorrect_cvcThe security code is incorrect.YesNo
three_ds_failed3D Secure authentication failed.YesNo
fraud_suspectedThe bank found the transaction suspicious.NoNo
not_permittedThe card is closed to this transaction (e.g. online shopping).YesNo
limit_exceededThe transaction limit was exceeded.YesNo
installment_not_allowedThe chosen instalment is not available for this card.YesNo
foreign_card_not_allowedForeign cards are not accepted.YesNo
cancelled_by_customerThe customer abandoned the payment.YesNo
session_expiredThe session expired.NoNo
provider_unavailableThe provider could not be reached.YesYes
provider_timeoutThe provider did not respond in time.YesYes
provider_rejectedThe provider rejected the request.YesYes
authentication_failedThe connection's credentials are invalid.NoYes
processing_errorA processing error at the provider.YesYes
risk_blockedThe business's risk rule stopped the payment before it reached the provider.NoNo

Test cards

The Efespay test provider is ready in test mode in every account. Each card triggers a scenario; no real money moves.

Test cards
Card numberScenarioCard
4242 4242 4242 4242Successful paymentVisa · Credit card
5555 5555 5555 4444Successful paymentMastercard · Debit card
4000 0000 0000 0002Card declinedVisa · Credit card
4000 0000 0000 9995Insufficient fundsVisa · Credit card
4000 0000 0000 32203D Secure failedVisa · Credit card
4000 0000 0000 0119Timeout, then success (verifying → succeeded)Visa · Credit card
4000 0000 0000 0127Timeout, then decline (verifying → failed)Visa · Credit card
  • Test cards work only in test mode and with the Efespay test provider.
  • No card number is entered on the test provider page; only the scenario you choose is sent to the server.
  • The timeout scenarios let you try the uncertain result and reconciliation.
  • Test events also arrive as webhooks; try your verification code before going live.

API reference

Every endpoint, field and example is in the OpenAPI document. The reference is published by the backend and updated together with the API.

Endpoint summary
EndpointScopePurpose
GET/v1Any keyVerifies the key and mode.
POST/v1/payment-sessionspayment_sessions:writeCreates a payment session.
GET/v1/payment-sessions/{id}payment_sessions:readReads a session.
GET/v1/paymentspayments:readLists payments (status, order reference, date filters).
GET/v1/payments/{id}payments:readReturns the verified payment status.
GET/v1/payments/{id}/attemptspayments:readReturns attempts with their routing reason.
POST/v1/payments/{id}/refundspayments:refundStarts a full or partial refund.
POST/v1/payments/{id}/cancelpayments:cancelCancels the payment (where the provider supports it).
POST/v1/paymentspayments:chargeCharges a saved card (Idempotency-Key required).
GET/v1/payment-methodspayment_methods:readLists a customer's saved cards.
DELETE/v1/payment-methods/{id}payment_methods:writeDeletes a saved card.
POST/v1/subscriptionssubscriptions:writeCreates a recurring subscription on a saved card.
GET/v1/subscriptions/{id}subscriptions:readReturns the subscription and its next charge.
POST/v1/subscriptions/{id}/cancelsubscriptions:writeCancels the subscription.
POST/v1/sub_merchantssub_merchants:writeAdds a marketplace seller (sub-merchant) and registers it with marketplace-enabled connections.
GET/v1/sub_merchantssub_merchants:readLists sub-merchants (IBAN and ID numbers masked).
POST/v1/payments/{id}/items/{item_id}/approvepayments:refundApproves a marketplace item; the seller share is released.
POST/v1/payments/{id}/items/{item_id}/disapprovepayments:refundDoesn't approve a marketplace item or withdraws its approval; the seller share is held.
GET/v1/provider-connectionsprovider_connections:readLists connections (without credentials).
POST/v1/payment-linkspayment_links:writeCreates a payment link.
GET/v1/payment-links/{id}/qrpayment_links:readReturns the link's QR code as SVG.
POST/v1/webhook-endpointswebhook_endpoints:writeCreates a webhook endpoint; the signing secret is returned once.
POST/v1/webhook-endpoints/{id}/testwebhook_endpoints:writeSends a ping event to the endpoint.
POST/v1/mcpAny keyMCP server for AI agents: payment lookups, payment links, refund requests (no refunds).

Let's plan the integration together.

Describe your current payment flow and technical setup; let's agree together on test environment access and pilot scope.