Kommo + Payoneer: International Payments from the Pipeline Without Manual Invoicing

With the right integration, Kommo sends a payment request through Payoneer the moment a deal moves to the “Invoice Sent” stage, and after payment confirmation the card automatically advances. The manager never logs into Payoneer manually, and the finance controller stops reconciling Excel spreadsheets.

For B2B companies working with clients in the US, Europe, and Asia, Payoneer is the primary tool for receiving international payments. The problem is that Kommo has no native integration with Payoneer, and third-party connectors through Zapier do not go deep enough: payment status does not update in real time, and transaction history does not appear in the deal timeline.

EU B2B companies working with clients outside the SEPA zone often choose Payoneer for its multi-currency wire transfers and lower fees compared to standard SWIFT. But when a deal is won in Kommo, initiating a payment request in Payoneer is always a manual step: open the platform, find the right payee, enter the amount and currency, copy the payment ID back into the CRM. With 50-100 transactions per month, that adds up to 10-20 hours of manager time spent on pure routine. This article covers the custom integration that triggers a payment request automatically on a Kommo pipeline stage change and syncs the status back into the deal card.

Why the native integration does not work

Payoneer is a payment platform for receiving international B2B payments, mass payouts, and managing multi-currency balances. As of Q2 2026 Payoneer does not provide a ready-made widget or plugin for Kommo.

A Zapier approach looks logical but runs into several limitations:

  • Payoneer API requires server-side OAuth 2.0 authorization - Zapier cannot reliably store a refresh token between requests
  • The Payoneer webhook for payment status (COMPLETED, FAILED, PENDING) needs to be listened on your own server - Zapier is not a stable webhook endpoint for production loads
  • Zapier fees at 200+ transactions per month make the solution more expensive than custom development

Make (formerly Integromat) handles webhooks somewhat better, but the mapping problem remains: Payoneer operates with the concepts “payer,” “program,” and “payment batch,” which do not match Kommo’s object model (deal, contact, company).

What gets built - solution architecture

The custom integration is built on three components:

  1. Webhook listener on your server receives events from Kommo (pipeline stage change)
  2. Payoneer API client creates a payment request and returns the payment ID
  3. Reverse webhook from Payoneer updates the deal field and adds a note to the timeline
Kommo (stage changed)
        |
        v
  Your server (webhook handler)
        |
        v
  Payoneer API (create payment request)
        |
        v
  Payoneer -> Your server (payment status webhook)
        |
        v
  Kommo API (update deal + add note)

Kommo deal fields used in the integration:

  • payoneer_payment_id - custom field storing the transaction ID
  • payment_status - PENDING / COMPLETED / FAILED
  • payment_amount - amount in the payment currency
  • payment_currency - USD / EUR / GBP

Technical details

Payoneer API v4 uses OAuth 2.0 with the client credentials flow. A token is issued for 3600 seconds and must be refreshed after that. Your server should cache the access token and refresh it proactively.

import requests
import time

class PayoneerClient:
    def __init__(self, client_id, client_secret, program_id):
        self.client_id = client_id
        self.client_secret = client_secret
        self.program_id = program_id
        self.base_url = "https://api.payoneer.com/v4"
        self._token = None
        self._token_expires = 0

    def _get_token(self):
        if time.time() < self._token_expires - 60:
            return self._token
        resp = requests.post(
            f"{self.base_url}/oauth2/token",
            data={
                "grant_type": "client_credentials",
                "client_id": self.client_id,
                "client_secret": self.client_secret,
                "scope": "read write"
            }
        )
        resp.raise_for_status()
        data = resp.json()
        self._token = data["access_token"]
        self._token_expires = time.time() + data["expires_in"]
        return self._token

    def create_payment_request(self, payee_id, amount, currency, description):
        headers = {"Authorization": f"Bearer {self._get_token()}"}
        payload = {
            "program_id": self.program_id,
            "payee_id": payee_id,
            "amount": amount,
            "currency": currency,
            "description": description,
            "payment_method": "bank_transfer"
        }
        resp = requests.post(
            f"{self.base_url}/payments",
            json=payload,
            headers=headers
        )
        resp.raise_for_status()
        return resp.json()["payment_id"]

Step-by-step implementation

Step 1. Set up a Payoneer Developer Account

Log in to the Payoneer Partner Portal, create an application, and get the client_id and client_secret. Note the program_id for your account - it is required for creating payments.

Step 2. Create the Kommo webhook endpoint

In the Kommo “Integrations” section, create a new webhook pointing to your server URL. Subscribe to the leads.status event - it fires when a pipeline stage changes.

Step 3. Deploy the event handler

from flask import Flask, request, jsonify
import hmac, hashlib

app = Flask(__name__)
payoneer = PayoneerClient(
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
    program_id="YOUR_PROGRAM_ID"
)

KOMMO_BILLING_STAGE_ID = 12345678  # ID of "Invoice Sent" stage

@app.route("/kommo/webhook", methods=["POST"])
def kommo_webhook():
    data = request.json
    for lead_update in data.get("leads", {}).get("status", []):
        lead_id = lead_update["id"]
        new_stage = lead_update["status_id"]
        if new_stage == KOMMO_BILLING_STAGE_ID:
            handle_billing_stage(lead_id)
    return jsonify({"ok": True})

def handle_billing_stage(lead_id):
    lead = kommo_api.get_lead(lead_id)
    payee_id = lead.custom_fields.get("payoneer_payee_id")
    amount = lead.custom_fields.get("payment_amount")
    currency = lead.custom_fields.get("payment_currency", "USD")
    
    payment_id = payoneer.create_payment_request(
        payee_id=payee_id,
        amount=float(amount),
        currency=currency,
        description=f"Invoice for deal #{lead_id}"
    )
    
    kommo_api.update_lead(lead_id, {
        "payoneer_payment_id": payment_id,
        "payment_status": "PENDING"
    })
    kommo_api.add_note(lead_id, f"Payoneer payment request created. ID: {payment_id}")

Step 4. Configure the inbound Payoneer webhook

In the Payoneer Partner Portal, specify the status webhook URL: https://yourserver.com/payoneer/status. Payoneer sends a POST request on every payment status change.

@app.route("/payoneer/status", methods=["POST"])
def payoneer_status():
    data = request.json
    payment_id = data["payment_id"]
    status = data["status"]  # COMPLETED, FAILED, PENDING
    
    # Find the deal by payment_id
    lead = kommo_api.find_lead_by_field("payoneer_payment_id", payment_id)
    if not lead:
        return jsonify({"error": "Lead not found"}), 404
    
    kommo_api.update_lead(lead["id"], {"payment_status": status})
    
    if status == "COMPLETED":
        kommo_api.move_to_stage(lead["id"], PAID_STAGE_ID)
        kommo_api.add_note(lead["id"], 
            f"Payoneer: payment received. Amount: {data['amount']} {data['currency']}")
    elif status == "FAILED":
        kommo_api.add_note(lead["id"], 
            f"Payoneer: payment declined. Reason: {data.get('failure_reason', 'unknown')}")
    
    return jsonify({"ok": True})

Step 5. Add custom fields in Kommo

In Kommo settings, create custom deal fields: payoneer_payee_id (text), payoneer_payment_id (text), payment_status (dropdown: PENDING/COMPLETED/FAILED), payment_amount (number), payment_currency (dropdown: USD/EUR/GBP). Attach the fields to the relevant pipeline stages for convenient display.

Real case with numbers

Berlin-based digital agency working with clients in the US and Australia, processing 80-120 international payments per month. Before the integration, the invoicing cycle took:

  • Manually opening Payoneer: 3-5 minutes
  • Finding the right payee and entering the amount: 5-7 minutes
  • Copying the payment ID into the CRM: 2-3 minutes
  • Manual status check after 2-3 days: 10-15 minutes per week in total

Total: 10-15 minutes per invoice plus ongoing monitoring. With 100 payments per month - roughly 20 hours of manager time.

After implementing the custom integration via Exceltic.dev:

  • Payment request is created automatically on stage transition: 0 minutes of manual work
  • COMPLETED status updates the deal and moves it forward: 0 minutes
  • Manager sees the payment history in the card timeline without logging into Payoneer

Time saved: 18-20 hours per month. Plus it eliminated an error that occurred 2-3 times per quarter: a manager forgetting to update the CRM status so the deal sat on “Waiting for payment” for weeks.

For guidance on setting up a Kommo pipeline including payment stages, see the dedicated article.

Who this is for

The Kommo + Payoneer integration is relevant for:

  • Agencies and freelancers working with international clients and receiving payment in foreign currency
  • SaaS companies where some clients pay by bank transfer rather than card
  • Service B2B with a long deal cycle where 2-4 weeks pass between invoicing and payment
  • Companies with 50+ transactions per month where manual work has become a bottleneck

If you work with a subscription model and need recurring billing logic, look at Kommo + Chargebee or Recurly. For one-off in-CRM card payments Stripe is a better fit. Payoneer is optimal specifically for international B2B wire transfers.

For more on Kommo CRM features and automation possibilities, see the overview article.

Frequently asked questions

Does Payoneer support an API for creating payment requests?

Yes. Payoneer provides REST API v4 with support for creating payments, retrieving statuses, and configuring webhook notifications. API documentation is available at developer.payoneer.com. Access requires registering as a Payoneer Partner and completing account verification, which typically takes 3-5 business days.

How does the integration handle multiple currencies?

Payoneer supports multi-currency payments: USD, EUR, GBP, JPY, and others. The payment currency is specified in a custom deal field in Kommo and passed to the API when creating the request. Conversion happens on the Payoneer side at the current rate. The manager sees the amount in the original currency in the deal card.

What if the Payoneer webhook does not arrive?

A proper architecture includes idempotency: the webhook endpoint returns 200 even on duplicate delivery of the same event. Additionally, a cron job polls the status of unclosed payments via the Payoneer API once an hour and syncs it to Kommo. This guarantees data accuracy even during temporary delivery failures.

Can automatic payment reminders be sent to the client?

Yes. After creating a payment request, the integration can trigger Kommo automation: send an email or SMS reminder after 3 and 7 days if the status remains PENDING. This is done through custom Kommo integrations - Kommo’s standard automation triggers on custom field changes.

How long does integration development take?

A standard Kommo + Payoneer project with basic functionality (create payment request, update status, timeline notes) takes 2-3 weeks. More complex scenarios - mass payouts, multi-currency logic, accounting system integration - require 4-6 weeks.


If you process international payments through Payoneer and want to eliminate the manual work - describe your requirements to the Exceltic.dev team. We will design the architecture for your stack and estimate the scope.

More articles

All →