Recipe

WhatsApp agent with Pix payments

Collect Pix with a Tyxter payment request through Abacate Pay — the recommended merchant payment path — or send Meta's native Pix message from an eligible Brazil WABA.

These paths have different settlement owners. The recommended Abacate path creates a Tyxter payment request, and its payment.paid event confirms settlement. A native WhatsApp Pix message does not create a Tyxter payment request or reconcile payment state.

Recommended: transparent Abacate Pix

This is the recommended merchant Pix path. It works for every WhatsApp Business Account — no Meta payments onboarding is involved — and settlement is confirmed by the Tyxter payment request's own payment.paid event. POST /v1/payments with provider_options.abacate_pay.charge_type = "pix". Creation is asynchronous, so poll GET /v1/payments/{payment_id} until pix_copy_paste is non-null, then send it as a normal WhatsApp text message. First read GET /v1/provider-connections/status: Abacate Pay must be connected and selected as the environment default, and the returned channels.payments.active_mode must equal abacate_pay before you send provider_options.abacate_pay. An unresolved provider selection fails with payment_provider_selection_required; options that do not match the active provider fail with payment_provider_options_unsupported. Reading provider status requires the API key scope provider_connections:read; without it, the call fails with 403 insufficient_scope. The connectionless sandbox default does not emulate transparent Pix.

POST/v1/payments
GET/v1/payments/{payment_id}
const providerStatus = await tyxter.providerConnections.status();
if (providerStatus.channels.payments.active_mode !== "abacate_pay") {
  throw new Error("Connect and select Abacate Pay before requesting transparent Pix");
}

const created = await tyxter.payments.create(
  {
    amount_brl_centavos: 12900,
    description: "Order #123",
    external_reference: "ord_123",
    provider_options: { abacate_pay: { charge_type: "pix" } },
  },
  { idempotencyKey: crypto.randomUUID() },
);

let payment = created;
for (let attempt = 0; attempt < 30 && !payment.pix_copy_paste; attempt += 1) {
  if (
    payment.status === "failed" ||
    payment.status === "expired" ||
    payment.status === "cancelled"
  ) {
    throw new Error("Pix creation ended with " + payment.status);
  }
  await new Promise((resolve) => setTimeout(resolve, 1_000));
  payment = await tyxter.payments.retrieve(created.id);
}

if (!payment.pix_copy_paste) throw new Error("Pix code is still pending");

// This is a free-form WhatsApp text send and requires an open 24-hour
// customer-service window; otherwise POST /v1/messages returns service_window_required.
await tyxter.whatsapp.sendText({
  from: "phone_number_id",
  to: "+5511999999999",
  body: "Pay with Pix (copy and paste):\n" + payment.pix_copy_paste,
});

The final text send is free-form WhatsApp messaging, so it requires an open 24-hour customer-service window. Without one, Tyxter rejects POST /v1/messages synchronously with service_window_required; use an approved template outside the window. Subscribe to payment.paid for this Tyxter payment request. That event records merchant PSP settlement reported by Abacate Pay; it is not a Meta order-status signal.

Alternative: native WhatsApp Pix

The native interactive.order_details message is an optional in-chat checkout upgrade, not the primary Tyxter payment path. Use it only when Meta Payments API in Brazil is enabled for the sending WhatsApp Business Account; most integrations should use the Abacate path above. This is a free-form interactive message, so the conversation must have an open 24-hour customer-service window. Without one, Tyxter rejects synchronously with service_window_required. Tyxter returns 202 Accepted only after contract, authentication, feature, and service-window prevalidation; Meta then evaluates WABA and message eligibility at provider-send time. The acceptance response does not promise Meta delivery, so inspect normal message status and webhooks.

POST/v1/messages
{
  "channel": "whatsapp",
  "sender": { "type": "whatsapp_phone_number", "id": "phone_number_id" },
  "recipient": { "type": "phone_e164", "id": "+5511999999999" },
  "message": {
    "type": "interactive",
    "interactive": {
      "type": "order_details",
      "body": { "text": "Review and pay your order." },
      "action": {
        "name": "review_and_pay",
        "parameters": {
          "reference_id": "ord_123-20260807",
          "type": "digital-goods",
          "payment_type": "br",
          "payment_settings": [{
            "type": "pix_dynamic_code",
            "pix_dynamic_code": {
              "code": "00020101021226700014br.gov.bcb.pix...",
              "merchant_name": "Example Store",
              "key": "39580525000189",
              "key_type": "CNPJ"
            }
          }],
          "currency": "BRL",
          "total_amount": { "value": 12900, "offset": 100 }
        }
      }
    }
  }
}

The caller supplies reference_id, the dynamic Pix code, merchant_name, key, and key_type. There is no payment_id lookup or order_status update in this message contract. Meta transports the payment details but does not reconcile merchant settlement; confirm payment with your merchant or PSP.

Tyxter endpoints

POST/v1/messages
POST/v1/webhook-endpoints
POST/v1/payments
GET/v1/payments/{payment_id}
POST/v1/sandbox/inbound-messages

LLM routing

Tyxter LLM routing is available for BYOK inbound automation, but it is optional for this architecture. Prefer app-owned LLM orchestration when the agent must call tools Tyxter does not own.