> ## 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.

# Quickstart

> Provision your aggregator, request a secret key, create a merchant, and send your first Raast payment.

Safepay Raast lets you orchestrate Request To Pay flows on behalf of your merchants. Follow this quickstart to get credentials, link a merchant, and send an RTP request.

## Prerequisites

* `{{aggregator_id}}` issued by Safepay
* `{{secret_key}}` for the target environment
* A Raast merchant to link during merchant creation

## Environments

| Environment | Base URL                                   | Notes                                                                  |
| ----------- | ------------------------------------------ | ---------------------------------------------------------------------- |
| Sandbox     | `https://dev.api.getsafepay.com/raastwire` | Use for integration testing. Data resets periodically.                 |
| Production  | `https://api.getsafepay.com/raastwire`     | Live traffic. Requests require enabled merchants and approved KYC/KYB. |

<Steps>
  <Step title="Request an aggregator account">
    Email [support@getsafepay.com](mailto:support@getsafepay.com) with your business context. Safepay provisions an [Aggregator](/concepts/aggregator), shares your `{{aggregator_id}}`, and issues the first `{{secret_key}}`.
  </Step>

  <Step title="Secure the aggregator secret key">
    Store `{{secret_key}}` in your secret manager and scope access to your backend jobs only. Every Raast API request must send the header `X-SFPY-AGGREGATOR-SECRET-KEY: {{secret_key}}`.
  </Step>

  <Step title="Link a merchant to a Raast merchant">
    Create an aggregator merchant with a unique `merchant_external_id`, settlement `iban`, and a [Raast merchant](/concepts/raast-merchants) token. The response returns an `aggregator_merchant_identifier` you will use in every payment request.
  </Step>

  <Step title="Trigger your first RTP payment">
    Use the [Create RTP](/api-reference#post-/v1/aggregators/-raast-aggregator-id-/payments) endpoint to request funds. Safepay sends status updates via [webhooks](/concepts/webhooks).
  </Step>
</Steps>

<Callout type="warning">Never ship `{{secret_key}}` in client-side code. Rotate credentials immediately if exposed.</Callout>

## Create your first merchant

Each merchant you manage must be linked to a Raast merchant. Set `{{base_url}}` to `https://dev.api.getsafepay.com/raastwire` in Sandbox or `https://api.getsafepay.com/raastwire` in Production.

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST "{{base_url}}/v1/aggregators/{{aggregator_id}}/merchants" \
    --header "X-SFPY-AGGREGATOR-SECRET-KEY: {{secret_key}}" \
    --header "Content-Type: application/json" \
    --data '{
      "merchant_external_id": "sec_0c3de397-441f-471f-bcd7-b6d948e1c307",
      "iban": "PK62ABPA0010000222380013",
      "name": "Acme Foods Saddar",
      "enabled": true,
      "raast_merchant_id": "{{raast_merchant_id}}",
      "rate_card": {
        "ratecard_kind": "RateCardKind_fixed",
        "fixed_rate": 3000,
        "tax_region": "PK",
        "tax_rate": 0.1
      }
    }'
  ```

  ```typescript TypeScript theme={null}
  const baseUrl = '{{base_url}}';
  const aggregatorId = '{{aggregator_id}}';
  const secretKey = process.env.SAFEPAY_SECRET_KEY ?? '';
  const raastMerchantId = '{{raast_merchant_id}}';

  const createMerchantResponse = await fetch(
    `${baseUrl}/v1/aggregators/${aggregatorId}/merchants`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-SFPY-AGGREGATOR-SECRET-KEY': secretKey,
      },
      body: JSON.stringify({
        merchant_external_id: 'sec_0c3de397-441f-471f-bcd7-b6d948e1c307',
        iban: 'PK62ABPA0010000222380013',
        name: 'Acme Foods Saddar',
        enabled: true,
        raast_merchant_id: raastMerchantId,
        rate_card: {
          ratecard_kind: 'RateCardKind_fixed',
          fixed_rate: 3000,
          tax_region: 'PK',
          tax_rate: 0.1,
        },
      }),
    },
  );

  if (!createMerchantResponse.ok) {
    const errorBody = await createMerchantResponse.json();
    throw new Error(`Safepay Raast error: ${createMerchantResponse.status} ${errorBody.message}`);
  }

  type CreateMerchantPayload = {
    api_version: string;
    data: {
      token: string;
      merchant_external_id: string;
      enabled: boolean;
      name: string;
    };
  };

  const merchantPayload: CreateMerchantPayload = await createMerchantResponse.json();
  const aggregatorMerchantIdentifier = merchantPayload.data.token;
  console.log('Aggregator merchant identifier', aggregatorMerchantIdentifier);
  ```
</CodeGroup>

<Callout type="tip">Persist `data.token`; Safepay uses it as the `aggregator_merchant_identifier` in subsequent payment, QR, and payout calls.</Callout>

## Make a payment request

Once Safepay enables the merchant, create a Request-to-Pay (RTP) using the same credentials. Reuse `{{base_url}}` for the target environment. The example below demonstrates an RTP Now payment that immediately prompts the customer.

<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": 5000,
      "aggregator_merchant_identifier": "{{aggregator_merchant_identifier}}",
      "order_id": "INV-2025-0001",
      "type": "RTP_NOW",
      "expiry_in_minutes": 30,
      "debitor_iban": "PK36SCBL0000001123456702"
    }'
  ```

  ```typescript TypeScript theme={null}
  const aggregatorMerchantIdentifier = process.env.SAFEPAY_AGGREGATOR_MERCHANT_IDENTIFIER;
  if (!aggregatorMerchantIdentifier) {
    throw new Error('Missing SAFEPAY_AGGREGATOR_MERCHANT_IDENTIFIER');
  }

  const baseUrl = '{{base_url}}';
  const aggregatorId = '{{aggregator_id}}';
  const secretKey = process.env.SAFEPAY_SECRET_KEY ?? '';

  const paymentResponse = 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: 5000,
        aggregator_merchant_identifier: aggregatorMerchantIdentifier,
        order_id: 'INV-2025-0001',
        type: 'RTP_NOW',
        expiry_in_minutes: 30,
        debitor_iban: 'PK36SCBL0000001123456702',
      }),
    },
  );

  if (!paymentResponse.ok) {
    const error = await paymentResponse.json();
    throw new Error(`Payment failed (${paymentResponse.status}): ${error.message}`);
  }

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

  const paymentPayload: CreatePaymentPayload = await paymentResponse.json();
  console.log('Payment token', paymentPayload.data.token);
  ```
</CodeGroup>

Next, subscribe to [webhooks](/concepts/webhooks) to know when the customer approves or rejects the request, and retry idempotently by reusing the same `request_id` when network errors occur.

## See also

* [Create aggregator account](/overview/create-aggregator)
* [Manage aggregator secret keys](/overview/request-access-key)
* [First API call](/overview/first-api-call)
* [Payment lifecycle guide](/guides/payment-lifecycle)
* [Webhooks](/concepts/webhooks)
