← All ivy-agents
IVYXSTUDIO · IVY AGENT
P

Payment Failed Dunning

ivy.agent.payment-failed-dunning · v1.0.0

ivyx

Takes a failed-payment event, looks the customer up in the CRM for the account owner, mails the customer a reminder and flags the account through your API.

Steps

In the order the agent's file lists them, each with where its inputs come from and the values the file fixes. A branch or a loop is a step too.

  1. 01
    Find the account owner
    ivy.node.http-request

    Sends one HTTP request to a URL and returns the status, headers and body, parsed as JSON when the response is JSON.

    takes
    url from the agent's input, crm_url
    params.email from the agent's input, payload.customer_email
    set
    method GET
    secret
    token the stored secret crm-api-key
    returns
    owner
    Ivy Node
  2. 02
    Write the reminder
    ivy.node.template-render

    Fills {{ name }} placeholders in a text template from a mapping, and reports which names were missing.

    takes
    values from the agent's input, payload
    set
    template Hello {{ customer_name }}, Our attempt {{ attempt }} to charge {{ amount }} {{ currency }} for your account did not g...
    on_missing empty
    returns
    reminder
    Ivy Node
  3. 03
    Mail the customer
    ivy.node.email-send

    Sends a plain-text or HTML email through an SMTP server in one step and returns the recipients it went to.

    takes
    body from step 2, text
    to from the agent's input, payload.customer_email
    from_addr from the agent's input, username
    username from the agent's input
    smtp_host from the agent's input
    smtp_port from the agent's input
    use_tls from the agent's input, smtp_tls
    set
    smtp_host smtp.gmail.com
    smtp_port 587
    use_tls true
    subject Your payment did not go through
    secret
    password the stored secret mail-password
    returns
    sent
    Ivy Node
  4. 04
    Say why
    ivy.node.template-render

    Fills {{ name }} placeholders in a text template from a mapping, and reports which names were missing.

    takes
    values from the agent's input, payload
    set
    template Payment failed on attempt {{ attempt }} ({{ amount }} {{ currency }})
    on_missing empty
    Ivy Node
  5. 05
    Flag the account
    ivy.node.http-request

    Sends one HTTP request to a URL and returns the status, headers and body, parsed as JSON when the response is JSON.

    takes
    url from the agent's input, accounts_url
    json.id from the agent's input, payload.account_id
    json.owner from step 1, json.owner_email
    json.reason from step 4, text
    set
    method PATCH
    secret
    token the stored secret saas-api-key
    returns
    flagged
    Ivy Node

Nodes it brings

Adding this agent in IVYX Studio adds these nodes with it. A node your workspace already has is kept as it is, even at another version.

What it touches

Collected from what each of its nodes declares, plus the model call when a step is a model turn. A declaration is the author's statement, and it is what policy rules select on.

message-sendNetwork

What it needs

  • A stored secret named crm-api-key. The agent's file carries the name, never the value.
  • A stored secret named mail-password. The agent's file carries the name, never the value.
  • A stored secret named saas-api-key. The agent's file carries the name, never the value.

Inputs

FieldTypeDescription
payloadrequiredobjectThe event your service posted: customer_email, customer_name, account_id, amount, currency and attempt of the failed charge.
crm_urlrequiredstringThe CRM's contacts endpoint; a GET with ?email= returns the contact with its owner_email. The stand-in's address is used in the cases.
accounts_urlrequiredstringYour API's accounts endpoint; a PATCH with id, owner and reason flags the account. The stand-in's address is used in the cases.
usernamerequiredstringThe sending address, which is the login name; its password is the stored secret mail-password.
smtp_hoststringThe SMTP server; smtp.gmail.com when left out.
smtp_portintegerThe SMTP port; 587 when left out.
smtp_tlsbooleanUpgrade SMTP with STARTTLS; on when left out.

Outputs

FieldTypeDescription
ownerstringThe account owner's address, from the CRM.
sentbooleanTrue once the server accepted the reminder.
flaggedbooleanTrue once your API accepted the flag.
reminderstringThe reminder's text.

Tests

2 of 2 test cases passed on Oct 7, 2026, in the publisher's own environment, before this version was published. The registry keeps that record; it does not run the cases again.

Requires: python:3.9, the CRM's API key stored as the secret crm-api-key, your own API's key stored as the secret saas-api-key, an SMTP server (Gmail unless smtp_host says otherwise) with the sending account's password stored as the secret mail-password, for the cases, GreenMail on 127.0.0.1:3025 (mkdata-mail.py), for the cases, the stand-in API on 127.0.0.1:18910 (api-standin.py), which the secrets crm-1, gh-1, saas-1 and the stand-in's /slack/hook address satisfy

  • second-attempt

    The customer is mailed, the owner is read from the CRM and the account is flagged.

    given
    smtp_host 127.0.0.1
    smtp_port 3025
    smtp_tls false
    payload {"customer_email":"ada@acme.io","customer_name":"Ada Lovelace","account_id":"A-77","amount":490,"currency":"USD","att...
    crm_url http://127.0.0.1:18910/crm/contacts
    accounts_url http://127.0.0.1:18910/accounts
    username agent@ivyx.test
    expects
    owner equals owner@acme.io
    sent equals true
    flagged equals true
    reminder matches attempt 2 to charge 490 USD
  • another-customer

    Another customer's owner comes from that customer's domain.

    given
    smtp_host 127.0.0.1
    smtp_port 3025
    smtp_tls false
    payload {"customer_email":"ops@globex.example","customer_name":"Globex Ops","account_id":"A-78","amount":1200,"currency":"EUR...
    crm_url http://127.0.0.1:18910/crm/contacts
    accounts_url http://127.0.0.1:18910/accounts
    username agent@ivyx.test
    expects
    owner equals owner@globex.example
    sent equals true
    flagged equals true
    reminder matches 1200 EUR