# Importing Mandates

Source: https://docs.gocardless.com/docs/collect-payments/setting-up-mandates/importing-mandates

# Importing Mandates

## Why import mandates?

Importing mandates allows you to migrate existing Direct Debit authorisations from another provider into GoCardless without requiring your customers to re-authorise. Once imported, you can manage and collect against them alongside any mandates created directly through GoCardless.

> **Info:**
> This feature must be enabled on your account before use. Contact
>   [support@gocardless.com](mailto:support@gocardless.com) or your account manager to request access.

> **Info:**
> Each mandate import can only contain mandates from a single scheme. If you're migrating mandates
>   across multiple schemes, you'll need a separate import for each.

## How it works

1. You create a Mandate Import specifying the scheme
2. You add an entry for each mandate you want to import
3. You submit the import for review
4. GoCardless reviews and approves the import
5. Mandates are created, and you reconcile them with your internal records

> **Info: What happens to your customers**
> Customers do not need to take any action. Their mandate reference may change — if your scheme
>   requires this, you'll need to notify customers of the new reference in line with your scheme
>   rules.

## Step-by-step guide

### Create a Mandate Import

```http
POST https://api.gocardless.com/mandate_imports HTTP/1.1

}

HTTP/1.1 201 (Created)
Location: /mandate_imports/IM000010790WX1

}
```

```php
$client = new \GoCardlessPro\Client(array(
'access_token' => 'your_access_token_here',
'environment' => \GoCardlessPro\Environment::SANDBOX
));

$mandateImport = $client->mandateImports()->create([
"params" => ["scheme" => "sepa_core"]
]);
```

```python

client = gocardless_pro.Client(access_token="your_access_token_here", environment='sandbox')

mandate_import = client.mandate_imports.create(params=);
```

```ruby
@client = GoCardlessPro::Client.new(
access_token: "your_access_token",
environment: :sandbox
)

mandate_import = @client.mandate_imports.create(params: )
```

```java

String accessToken = "your_access_token_here";

GoCardlessClient client = GoCardlessClient
.newBuilder(accessToken)
.withEnvironment(SANDBOX)
.build();

MandateImport mandateImport = client.mandateImports().create()
.withScheme(SEPA_CORE)
.execute();
```

```javascript
const accessToken = "your_access_token";
const constants = require("gocardless-nodejs/constants");
const client = require("gocardless-nodejs")(accessToken, constants.Environments.Sandbox);

const mandateImport = await client.mandateImports.create();
```

```net
String accessToken = "your_access_token";
GoCardlessClient gocardless = GoCardlessClient.Create(accessToken, Environment.SANDBOX);

var importRequest = new GoCardless.Services.MandateImportCreateRequest()
;

var importResponse = await gocardless.MandateImports.CreateAsync(importRequest);
GoCardless.Resources.MandateImport mandateImport = importResponse.MandateImport;
```

```go
ctx := context.TODO()
mandateImportCreateParams := goCardless.MandateImportCreateParams
client.MandateImports().Create(ctx, mandateImportCreateParams)
```

Hold onto the ID, you'll need it to add entries and submit.

### Add Mandate Import Entries

Add one entry per mandate. Each entry requires:

- `links.mandate_import:` the ID from Step 1
- `customer:` identifying details for the customer
- `bank_account:` account holder name and either an IBAN or [local bank details]
- `record_identifier:` Your own reference for this mandate (see below)

> **Warning: Set record_identifier for every entry**
> This is your reference string that links each entry back to your internal records. Without it,
>   reconciling your existing customer IDs with the new GoCardless IDs after the import is processed
>   is significantly harder. It must be unique within the mandate import.

SEPA mandates only: You must also provide amendment details, the original mandate reference, creditor ID, and creditor name from your previous provider. These are required by the SEPA scheme to maintain a valid audit trail.

```php
$mandateImportEntry = $client->mandateImportEntries()->create([
  "params" => [
    "links" => [
      "mandate_import" => $mandateImport->id
    ],
    "record_identifier" => "bank-file.xml/line-1",
    "customer" => [
      "company_name" => "Théâtre du Palais-Royal",
      "email" => "moliere@tdpr.fr"
    ],
    "bank_account" => [
      "account_holder_name" => "Jean-Baptiste Poquelin",
      "iban" => "FR14BARC20000055779911"
    ],
    // Amendment details are required for SEPA only
    "amendment" => [
      "original_mandate_reference" => "REFNMANDATE",
      "original_creditor_id" => "FR123OTHERBANK",
      "original_creditor_name" => "Amphitryon"
    ]
  ]
]);
```

```python
mandate_import_entry = client.mandate_import_entries.create(params=,
  "record_identifier": "bank-file.xml/line-1",
  "customer": ,
  "bank_account": ,
  # Amendment details are required for SEPA only
  "amendment": 
})
```

```ruby
mandate_import_entry = @client.mandate_import_entries.create(params: ,
  record_identifier: "bank-file.xml/line-1",
  customer: ,
  bank_account: ,
  # Amendment details are required for SEPA only
  amendment: 
})
```

```java
MandateImportEntry mandateImportEntry = client.mandateImportEntries().create()
  .withCustomerCompanyName("Théâtre du Palais-Royal")
  .withCustomerEmail("moliere@tdpr.fr")
  .withBankAccountAccountHolderName("Jean-Baptiste Poquelin")
  .withBankAccountIban("FR14BARC20000055779911")
  // Amendment details are required for SEPA only
  .withAmendmentOriginalMandateReference("REFNMANDATE")
  .withAmendmentOriginalCreditorId("FR123OTHERBANK")
  .withAmendmentOriginalCreditorName("Amphitryon")
  .withLinksMandateImport(mandateImport.getId())
  .execute();
```

```javascript
const request = ,
  bank_account: ,
  // Amendment details are required for SEPA only
  amendment: ,
  links: ,
};

const mandateImportEntry = await client.mandateImportEntries.create(request);
```

```net
var request = new GoCardless.Services.MandateImportEntryCreateRequest()

  BankAccount = new GoCardless.Services.MandateImportEntryCreateRequest.MandateImportEntryBankAccount()
  
  // Amendment details are required for SEPA only
  Amendment = new GoCardless.Services.MandateImportEntryCreateRequest.MandateImportEntryAmendment()
  
  Links = new GoCardless.Services.MandateImportEntryCreateRequest.MandateImportEntryLinks()
  
};

var importResponse = await gocardless.MandateImportEntries.CreateAsync(request);
GoCardless.Resources.MandateImportEntry entry = importResponse.MandateImportEntry;
```

```go
ctx := context.TODO()
mandateImportEntryCreateParams := gocardless.MandateImportEntryCreateParams,
	BankAccount: gocardless.MandateImportEntryCreateParamsBankAccount,
	Amendment: &gocardless.MandateImportEntryCreateParamsAmendment,
	Links: gocardless.MandateImportEntryCreateParamsLinks,
}
client.MandateImportEntries().Create(ctx, mandateImportEntryCreateParams)
```

```http
POST https://api.gocardless.com/mandate_import_entries HTTP/1.1
,
    "record_identifier": "bank-file.xml/line-1",
    "customer": ,
    "bank_account": ,
    "amendment": 
  }
}

HTTP/1.1 201 (Created)

  }
}
```

Repeat for each mandate. If you provide invalid data, the API returns a descriptive error for that entry — fix it and retry before submitting.

SEPA example with amendment details:

```http
  curl -X POST https://api.gocardless.com/mandate_import_entries \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d ',
        "record_identifier": "your-internal-customer-id-456",
        "customer": ,
        "bank_account": ,
        "amendment": 
      }
    }
```

### Submit for Review

Once all entries are added, submit the import. All imports are reviewed by GoCardless before processing to prevent fraudulent use.

```php
$client->mandateImports()->submit($mandateImport->id);
```

```python
client.mandate_imports.submit(mandate_import.id)
```

```ruby
@client.mandate_imports.submit(mandate_import.id)
```

```java
client.mandateImports().submit(mandateImport.getId()).execute();
```

```javascript
await client.mandateImports.submit(mandateImport.id);
```

```net
await gocardless.MandateImports.SubmitAsync(mandateImport.Id);
```

```go
ctx := context.TODO()
mandateImportSubmitParams := goCardless.MandateImportSubmitParams
client.MandateImports().Submit(ctx, mandateImport.Id, mandateImportSubmitParams)
```

```http
POST https://api.gocardless.com/mandate_imports/IM000010790WX1/actions/submit HTTP/1.1

HTTP/1.1 200 (OK)

}
```

> **Info: Need to cancel?**
> You can cancel a submitted import before it is approved using the [cancellation API]. Once
>   approved, an import cannot be reversed.

### Await Approval

GoCardless reviews all submitted imports before processing. During this time the import status will be submitted.

Common reasons for rejection:

- Entries contain invalid or mismatched bank details
- SEPA amendment details are missing or incorrect
- The scheme specified doesn't match the bank accounts provided

If your import is rejected, GoCardless will contact you with details. You'll need to create a new import with corrected entries.

### Reconcile your records

When the import is approved, GoCardless processes the mandates. You'll receive webhooks as each mandate is created, the same events as mandates created through the standard API:

| **Event**         | **Meaning**                                              |
| ----------------- | -------------------------------------------------------- |
| `mandate.created` | Mandate successfully imported                            |
| `mandate.active`  | Mandate active and ready to collect against              |
| `mandate.failed`  | Mandate could not be created (e.g. invalid bank details) |

To map your internal records to the new GoCardless IDs, retrieve the entries once the import status is processed. Match each entry using the `record_identifier` you set in Step 2:

```php
$entries = $client->mandateImportEntries()->all([
  "params" => ["mandate_import" => "IM000010790WX1"]
]);

foreach ($entries as $i => $entry) 
```

```python
response = client.mandate_import_entries.all(
params=
).records

for entry in records:
print(entry.record_identifier)
print(entry.links.customer)
print(entry.links.customer_bank_account)
print(entry.links.mandate)
```

```ruby
@client.mandate_import_entries.all(
params: 
).each do |entry|
puts entry.record_identifier
puts entry.links.customer
puts entry.links.customer_bank_account
puts entry.links.mandate
end
```

```java
for (MandateImportEntry entry : client.mandateImportEntries().all().withMandateImport("IM000010790WX1").execute()) 
```

```javascript
const request = ;

for await (let entry of client.mandateImportEntries.all(request)) 
```

```net
var request = new GoCardless.Services.MandateImportEntryListRequest()
;

var response = gocardless.MandateImportEntries.All(request);
foreach (GoCardless.Resources.MandateImportEntry entry in response)

```

```go
ctx := context.TODO()
mandateImportEntryListParams := goCardless.MandateImportEntryListParams
mandateImportEntries, err := client.MandateImportEntries().List(ctx, mandateImportEntryListParams)
if err != nil 
for entry := range mandateImportEntries 
```

```http
GET https://api.gocardless.com/mandate_import_entries?mandate_import=IM000010790WX1 HTTP/1.1

HTTP/1.1 200 (OK)

    },
    
    }
  ],
  "meta": ,
    "limit": 50
  }
}
```

Use the mandate ID from each entry to start collecting payments. See [One-off payments](/docs/collect-payments/one-off-payments/one-off-direct-debit) and [Recurring payments](/docs/collect-payments/recurring-payments/subscriptions).

## What's next?

  
#### [Setting Up Mandates](/docs/collect-payments/setting-up-mandates)

Create new mandates from scratch using the Billing Requests API.

  
#### [One-off Direct Debit](/docs/collect-payments/one-off-payments/one-off-direct-debit)

Collect one-off payments against your newly imported mandates.

  
#### [Subscriptions](/docs/collect-payments/recurring-payments/subscriptions)

Set up recurring subscription payments against imported mandates.

  
#### [Mandate Events](/docs/collect-payments/events-and-webhooks/mandate-events)

Handle mandate lifecycle webhooks as imported mandates become active.