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

# RTP Later payment

> Schedule an RTP_LATER payment that gives the customer more time to approve.

Use this flow when customers may approve hours or days after you submit the payment, such as invoice settlements or subscription renewals.

## Prerequisites

* `{{aggregator_id}}` and `{{secret_key}}`
* `aggregator_merchant_identifier` for the merchant collecting funds
* Debtor IBAN or Raast ID

## Flow summary

<Steps>
  <Step title="Load merchant identifiers">
    Retrieve the `aggregator_merchant_identifier` from the merchant creation response.
  </Step>

  <Step title="Validate the debtor account">
    Use the [account validation utilities](/concepts/account-validation) to confirm the debtor details.
  </Step>

  <Step title="Create the RTP Later payment">
    Submit `POST /v1/aggregators/{{aggregator_id}}/payments` with `type: RTP_LATER` and an expiry window.
  </Step>

  <Step title="Track the outcome">
    Use webhooks (and optional polling) to surface the final payment status.
  </Step>
</Steps>

<Callout type="info">Set `{{base_url}}` to `https://dev.api.getsafepay.com/raastwire` in Sandbox or `https://api.getsafepay.com/raastwire` in Production.</Callout>

## RTP later

<Tabs>
  <Tab title="Request Fields">
    <ParamField path="request_id" type="string" required>
      UUID that keeps the scheduled Request To Pay idempotent.
    </ParamField>

    <ParamField path="amount" type="integer" required>
      Amount in paisa. `22000` equals PKR 220.00.
    </ParamField>

    <ParamField path="aggregator_merchant_identifier" type="string" required>
      Token returned when you created the merchant.
    </ParamField>

    <ParamField path="order_id" type="string" required>
      Merchant order reference used for reconciliation.
    </ParamField>

    <ParamField path="type" type="string" required>
      Set to `RTP_LATER`.
    </ParamField>

    <ParamField path="expiry_in_days" type="integer" required>
      Approval window in days (1-40).
    </ParamField>

    <ParamField path="debitor_raast_id" type="string">
      Provide when requesting via a Raast alias. One of the debtor identifiers must be supplied.
    </ParamField>

    <ParamField path="debitor_iban" type="string">
      Use when you know the debtor's IBAN instead of a Raast ID. One of the debtor identifiers must be supplied.
    </ParamField>

    <ParamField path="debitor_vault_token" type="string">
      Reference a stored payment method for recurring requests. One of the debtor identifiers must be supplied.
    </ParamField>
  </Tab>

  <Tab title="API Example">
    <CodeGroup>
      ```bash cURL theme={null}
      curl --request POST "{{base_url}}/v1/aggregators/{{aggregator_id}}/payments" \
        --header "X-SFPY-AGGREGATOR-SECRET-KEY: {{secret_key}}" \
        --header "Content-Type: application/json" \
        --data '{
          "request_id": "{{request_id}}",
          "amount": 22000,
          "aggregator_merchant_identifier": "{{aggregator_merchant_identifier}}",
          "order_id": "ORDER-INVOICE-2025-07",
          "type": "RTP_LATER",
          "expiry_in_days": 7,
          "debitor_raast_id": "03202296111"
        }'
      ```

      ```typescript TypeScript theme={null}
      const baseUrl = '{{base_url}}';
      const aggregatorId = '{{aggregator_id}}';
      const secretKey = '{{secret_key}}';
      const aggregatorMerchantIdentifier = '{{aggregator_merchant_identifier}}';

      const rtpLaterResponse = await fetch(
        `${baseUrl}/v1/aggregators/${aggregatorId}/payments`,
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-SFPY-AGGREGATOR-SECRET-KEY': secretKey,
          },
          body: JSON.stringify({
            request_id: crypto.randomUUID(),
            amount: 22000,
            aggregator_merchant_identifier: aggregatorMerchantIdentifier,
            order_id: 'ORDER-INVOICE-2025-07',
            type: 'RTP_LATER',
            expiry_in_days: 7,
            debitor_raast_id: '03202296111',
          }),
        },
      );

      if (!rtpLaterResponse.ok) {
        const errorPayload = await rtpLaterResponse.json();
        throw new Error(`RTP later failed: ${rtpLaterResponse.status} ${errorPayload.message}`);
      }

      type CreatePaymentPayload = {
        api_version: string;
        data: {
          token: string;
          status: 'P_INITIATED' | 'P_RECEIVED' | 'P_FAILED' | 'P_REJECTED' | 'P_SETTLED';
          request_id: string;
          order_id: string;
          trace_reference?: string;
          msg_id?: string;
          created_at: string;
        };
      };

      const rtpLaterPayload: CreatePaymentPayload = await rtpLaterResponse.json();
      console.log('Scheduled RTP token', rtpLaterPayload.data.token);
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Sample Response">
    ```json theme={null}
    {
      "api_version": "v1",
      "data": {
        "message": "RTP created successfully",
        "token": "pm_f531c5eb-5c49-40d6-8ac5-012cfd1b24c1",
        "msg_id": "IG2Y50ORB7BE2OLEW0HDVJUIP9QGTL1V6WS",
        "trace_reference": "aa22cc9b-3f55-4613-86cf-9641f11902d1",
        "uetr": "8df9cc1f-77b2-47e4-9a8b-a3050bee6972",
        "status": "P_INITIATED",
        "order_id": "ORDER-INVOICE-2025-07",
        "request_id": "{{request_id}}",
        "created_at": "2025-06-26T10:58:17Z"
      }
    }
    ```
  </Tab>
</Tabs>

## Monitor status

Use webhooks as the primary source of truth. Polling is helpful for dashboards or fallback workflows.

```bash cURL theme={null}
curl --request GET "{{base_url}}/v1/aggregators/{{aggregator_id}}/payments/{{payment_token}}" \
  --header "X-SFPY-AGGREGATOR-SECRET-KEY: {{secret_key}}"
```

## Webhook events to expect

* `payment.created`
* `payment.pending_authorization`
* `payment.authorized`
* `payment.completed`
* `payment.settled`
* `payment.rejected`
* `payment.failed`
* `payment.voided`
* `payment.reversed`
* `payment.refunded` (if you issue refunds)
* `payment.refund_partial` (if you issue partial refunds)

When the webhook indicates `payment.settled`, mark the invoice paid. If the customer rejects, resend with a new `request_id` after you resolve the issue.

## Troubleshooting

* **Expired request** - Regenerate the RTP with a fresh `request_id` and `expiry_in_days`.
* **No response yet** - Offer manual refresh or status polling for long-lived requests.
* **Duplicate payload** - Reusing a `request_id` with different parameters returns an idempotency error; retry with the same body.

## See also

* [Payment lifecycle guide](/guides/payment-lifecycle)
* [Account validation](/concepts/account-validation)
* [Webhooks delivery](/guides/webhooks-delivery)
* [Errors](/reference/errors)
* [Create RTP](/api-reference#post-/v1/aggregators/-raast-aggregator-id-/payments)
