> ## Documentation Index
> Fetch the complete documentation index at: https://docs.natural.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> When building application code, use `@naturalpay/sdk` for TypeScript or `naturalpay` for Python.
> When operating Natural directly for a user, use the payments MCP at `https://mcp.natural.com`.
> When searching Natural documentation, use the docs MCP at `https://docs.natural.com/mcp`.
> Use the Natural CLI for terminal and CI automation.
> Use REST only for unsupported languages or when the user explicitly requests raw HTTP.
> Start integration development and testing in the sandbox. Do not use production unless the user explicitly requests it.
> For sandbox requests that require `customerPartyId`, list customers first. Reuse only the intended fixture; otherwise create a customer.
> REST and SDK amounts use integer minor units. Payments MCP amounts use decimal strings with a required currency code.

# Invite a customer

> Invite a specific customer or share an invitation link to connect your agents

Ask a customer to authorize your agents, by a one-to-one customer invitation (`adi_*`) or a shareable [invitation link](#share-an-invitation-link) (`ivl_*`). Either way, the customer chooses what to allow and which wallet the agent can use, and nothing moves until they accept. For the connection model, read the [Customers overview](/guides/concepts/customers).

<Snippet file="shared/prerequisites.mdx" />

<Snippet file="shared/cents-note.mdx" />

## Send the invitation

Invite a customer by email or phone with [`POST /customers/invitations`](/api-reference/customers/invite-customers). Natural emails the invitation; for a phone recipient, deliver `attributes.url` yourself. Each request takes up to 100 recipients and 50 agents. Invitations expire after 30 days unless you set `expiresAt` (up to 90 days).

<Note>
  In the sandbox, email and phone recipients return
  `customer_invitation_recipient_unsupported_in_sandbox`. Use [`POST
      /simulations/invite-customer`](/api-reference/simulations/create-test-customer), which creates a
  test customer and a pending invitation for each agent you name.
</Note>

<CodeGroup>
  ```python Python theme={null}
  from naturalpay import Natural

  client = Natural()
  invitations = client.customers.create_invitations(
      recipients=[{"type": "email", "value": "ops@bistroroma.com"}],
      agents=[
          {
              "agentId": "agt_019cd1798d637a4da75dce386343931d",
              "permissions": [
                  "payments.read",
                  "payments.create",
                  "external_accounts.create",
                  "wallets.balance",
                  "wallets.update",
                  "party.read",
                  "party.update",
              ],
              "limits": {"perTransaction": 100_000},
          }
      ],
  )

  for invitation in invitations.data:
      print(invitation.id, invitation.attributes.url)
  ```

  ```typescript TypeScript theme={null}
  import Natural from "@naturalpay/sdk";

  const client = new Natural();
  const invitations = await client.customers.createInvitations({
    recipients: [{ type: "email", value: "ops@bistroroma.com" }],
    agents: [
      {
        agentId: "agt_019cd1798d637a4da75dce386343931d",
        permissions: [
          "payments.read",
          "payments.create",
          "external_accounts.create",
          "wallets.balance",
          "wallets.update",
          "party.read",
          "party.update",
        ],
        limits: { perTransaction: 100_000 },
      },
    ],
  });

  for (const invitation of invitations.data) {
    console.log(invitation.id, invitation.attributes.url);
  }
  ```

  ```bash CLI theme={null}
  natural customers create-invitations --json '{
    "recipients": [{ "type": "email", "value": "ops@bistroroma.com" }],
    "agents": [{
      "agentId": "agt_019cd1798d637a4da75dce386343931d",
      "permissions": ["payments.read", "payments.create", "external_accounts.create", "wallets.balance", "wallets.update", "party.read", "party.update"],
      "limits": { "perTransaction": 100000 }
    }]
  }'
  ```

  ```text MCP theme={null}
  Invite ops@bistroroma.com to connect my Procurement Agent.
  Let it run money end to end for them, limited to $1,000 per transaction.
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.natural.com/customers/invitations \
    -H "Authorization: Bearer $NATURAL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "data": {
        "attributes": {
          "recipients": [{ "type": "email", "value": "ops@bistroroma.com" }],
          "agents": [{
            "agentId": "agt_019cd1798d637a4da75dce386343931d",
            "permissions": ["payments.read", "payments.create", "external_accounts.create", "wallets.balance", "wallets.update", "party.read", "party.update"],
            "limits": { "perTransaction": 100000 }
          }]
        }
      }
    }'
  ```
</CodeGroup>

The response carries one invitation (`adi_*`) per recipient and agent, each with `attributes.url`, the same URL Natural emails the customer. You can also surface it inside your own product; either path lands on the same acceptance page. Check `meta.failedRecipients`; a recipient can fail while others succeed.

<Snippet file="api-examples/customers.createInvitations.response.mdx" />

## Send the customer back to your product

Set `redirectUrl` on the invitation to bring the customer back once they are connected. A customer new to Natural is sent there after they accept and finish setup; a customer who already has an account is sent there right after they accept. Nothing happens if they decline, or while their account is still under review.

```json theme={null}
{
  "data": {
    "attributes": {
      "recipients": [{ "type": "phone", "value": "+14155550123" }],
      "agents": [
        { "agentId": "agt_019cd1798d637a4da75dce386343931d", "permissions": ["payments.read"] }
      ],
      "redirectUrl": "sms:+14155550199"
    }
  }
}
```

`redirectUrl` accepts an `https` URL (including universal links), an app deep link such as `acme://connected`, or an `sms:` link that reopens a Messages or iMessage thread. Natural rejects `http`, `javascript:`, `data:` and similar schemes. The redirect is a convenience for the customer; use the `agent_delegation_invitation.accepted` webhook to trigger your own follow-up, such as texting the customer their next steps.

## Share an invitation link

An invitation link is the same offer with no recipient: create it once, share the URL anywhere, and any customer who opens it can accept. Create one with [`POST /customers/invitation-links`](/api-reference/invitation-links/create-invitation-link), naming the agents it offers and what each one asks for. `name` is required and at most 32 characters. The create response is the only place the API returns the shareable `url`, so store it then. `expiresAt` is optional; a link without one stays open until you revoke it.

<CodeGroup>
  ```python Python theme={null}
  from naturalpay import Natural

  client = Natural()
  link = client.invitation_links.create(
      name="Q3 carrier onboarding",
      proposed_agents=[
          {
              "agentId": "agt_019cd1798d637a4da75dce386343931d",
              "permissions": ["payments.read", "payments.create"],
              "limits": {"perTransaction": 100_000},
          }
      ],
  )

  print(link.data.attributes.url)
  ```

  ```typescript TypeScript theme={null}
  import Natural from "@naturalpay/sdk";

  const client = new Natural();
  const link = await client.invitationLinks.create({
    name: "Q3 carrier onboarding",
    proposedAgents: [
      {
        agentId: "agt_019cd1798d637a4da75dce386343931d",
        permissions: ["payments.read", "payments.create"],
        limits: { perTransaction: 100_000 },
      },
    ],
  });

  console.log(link.data.attributes.url);
  ```

  ```bash CLI theme={null}
  natural invitation-links create --json '{
    "name": "Q3 carrier onboarding",
    "proposedAgents": [{
      "agentId": "agt_019cd1798d637a4da75dce386343931d",
      "permissions": ["payments.read", "payments.create"],
      "limits": { "perTransaction": 100000 }
    }]
  }'
  ```

  ```text MCP theme={null}
  Create an invitation link named "Q3 carrier onboarding" offering my Procurement Agent
  with payments access, limited to $1,000 per transaction.
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.natural.com/customers/invitation-links \
    -H "Authorization: Bearer $NATURAL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "data": {
        "attributes": {
          "name": "Q3 carrier onboarding",
          "proposedAgents": [{
            "agentId": "agt_019cd1798d637a4da75dce386343931d",
            "permissions": ["payments.read", "payments.create"],
            "limits": { "perTransaction": 100000 }
          }]
        }
      }
    }'
  ```
</CodeGroup>

[`GET /customers/invitation-links`](/api-reference/invitation-links/list-invitation-links) returns each link's offer and status with a masked token and no URL. [`GET /customers/invitation-links/{linkId}`](/api-reference/invitation-links/get-invitation-link) returns one link the same way. `status` is `ACTIVE` or `REVOKED`; a link past `expiresAt` stays `ACTIVE` in the list but no longer connects anyone. If you lose a link's URL, revoke the link and create a new one.

[`POST /customers/invitation-links/{linkId}/revoke`](/api-reference/invitation-links/revoke-invitation-link) closes the offer. It stops accepting new customers immediately; customers it already connected are untouched, because a link only ever offers, it never grants.

Every connection made through a link records its source: the `delegation.activated` webhook carries `sourceType: "link"` and, when known, `sourceId` set to the link's ID. Run one link per channel and count acceptances by `sourceId` to measure per-channel conversion.

Redeeming the same offer again for an already-connected customer and agent is a no-op: Natural
returns the existing connection and does not widen its permissions or limits. Disconnecting first
does not revive the old acceptance; reconnect with a fresh invitation or offer.

## What the customer does

<Steps>
  <Step title="Opens the link">
    They see who is requesting and which agents act. A revoked or expired invitation link shows only
    that it is no longer available.
  </Step>

  <Step title="Onboards if new">
    A customer new to Natural creates an account and verifies as part of accepting. Natural handles
    the compliance.
  </Step>

  <Step title="Picks a wallet and accepts">They choose which wallet the agent may use.</Step>
</Steps>

The invitation flips to `ACCEPTED` and the customer shows up on [`GET /customers`](/api-reference/customers/list-customers). An invitation link keeps working for the next customer; one link can onboard any number of them. Their `id` is the party ID (`pty_*`) you pass to act as them. From there the agent moves money for them within the permissions and limit they granted.

<Note>
  A new customer answers the invitation during signup, but the connection is created only once
  Natural approves their account. `agent_delegation_invitation.accepted` and `delegation.activated`
  arrive at approval, so the invitation stays `PENDING` while the customer is under review. A
  customer who skips the invitation during signup, or whose application is rejected, is not
  connected; a skipped invitation stays `PENDING` until they accept it from their dashboard or it
  expires.
</Note>

<Snippet file="shared/webhook-connect-invitation.mdx" />


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.