So far, we have discussed ways to help your users set up new recurring payments, add information about new customers and so on.

However, your users may also have existing direct debit mandates set up and wish to manage them together with their GoCardless mandates. The mandate imports API exists to allow integrators to automate this process seamlessly.

Getting set up to import mandates

If you wish to use this feature, you must first have it enabled on your merchant account. To request this, get in touch with your account manager or with our support team and we will respond to your request as soon as possible.

Once the feature is enabled on your account, you can start creating mandate imports. The procedure for using this feature is as follows in outline:

  1. Create a mandate import
  2. Add entries to the import
  3. Submit the import
  4. Wait until a member of the GoCardless team approves the import, at which point the mandates will be created
  5. Link up the mandates in your system

Creating a mandate import

The first step is simple. The only data needed for creating a mandate import is the direct debit scheme the mandates are using. Please note that a mandate import can only include mandates from one direct debit scheme, the one specified here. If you want to transfer mandates in several schemes, you will need one mandate import per scheme.

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

$mandateImport = $client->mandateImports()->create([
  "params" => ["scheme" => "sepa_core"]
]);
import gocardless_pro
client = gocardless_pro.Client(access_token="your_access_token_here", environment='sandbox')

mandate_import = client.mandate_imports.create(params={
  "scheme": "sepa_core"
});
@client = GoCardlessPro::Client.new(
  access_token: "your_access_token",
  environment: :sandbox
)

mandate_import = @client.mandate_imports.create(params: {
  scheme: "sepa_core"
})

import static com.gocardless.GoCardlessClient.Environment.SANDBOX;
import static com.gocardless.services.MandateImportService.MandateImportCreateRequest.Scheme.SEPA_CORE;

String accessToken = "your_access_token_here";

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


MandateImport mandateImport = client.mandateImports().create()
  .withScheme(SEPA_CORE)
  .execute();
String accessToken = "your_access_token";
GoCardlessClient gocardless = GoCardlessClient.Create(accessToken, Environment.SANDBOX);

var importRequest = new GoCardless.Services.MandateImportCreateRequest()
{
    Scheme = MandateImportCreateRequest.MandateImportScheme.SepaCore
};

var importResponse = await gocardless.MandateImports.CreateAsync(importRequest);
GoCardless.Resources.MandateImport mandateImport = importResponse.MandateImport;
POST https://api.gocardless.com/mandate_imports HTTP/1.1
{
  "mandate_imports": {
    "scheme": "sepa_core"
  }
}

HTTP/1.1 201 (Created)
Location: /mandate_imports/IM000010790WX1
{
  "mandate_imports": {
    "id": "IM000010790WX1",
    "scheme": "sepa_core",
    "status": "created",
    "created_at": "2018-03-12T14:03:04.000Z"
  }
}

Adding mandate import entries

Once we have created the mandate import itself, the next step is to add mandate import entries for each mandate we want to import. To create a mandate import entry, we must provide:

  • the ID of the mandate import we are populating
  • a customer sub-resource, containing identifying data about the customer to be charged by the mandate (required fields differ between schemes; see the API reference for details)
  • a bank_account sub-resource, containing the account holder name bank details (either an IBAN or local details)

For mandates in the SEPA scheme, you must also provide amendment details, which identify the current state of the mandate. See the code examples below and the API reference for details.

It is also possible - and recommended - to provide a record_identifier value, a string which should be unique per mandate import. This will make it easier to reconcile the imports with records in your own system later.

$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"
    ]
  ]
]);
mandate_import_entry = client.mandate_import_entries.create(params={
  "links": {
    "mandate_import": mandate_import.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"
  }
})
mandate_import_entry = @client.mandate_import_entries.create(params: {
  links: {
    mandate_import: mandate_import.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"
  }
})

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();
var request = new GoCardless.Services.MandateImportEntryCreateRequest()
{
  Customer = new GoCardless.Services.MandateImportEntryCreateRequest.MandateImportEntryCustomer()
  {
    CompanyName = "Théâtre du Palais-Royal"
    Email = "moliere@tdpr.fr"
  }
  BankAccount = new GoCardless.Services.MandateImportEntryCreateRequest.MandateImportEntryBankAccount()
  {
    AccountHolderName = "Jean-Baptiste Poquelin"
    Iban = "FR14BARC20000055779911"
  }
  // Amendment details are required for SEPA only
  Amendment = new GoCardless.Services.MandateImportEntryCreateRequest.MandateImportEntryAmendment()
  {
    OriginalMandateReference = "REFNMANDATE"
    OriginalCreditorId = "FR123OTHERBANK"
    OriginalCreditorName = "Amphitryon"
  }
  Links = new GoCardless.Services.MandateImportEntryCreateRequest.MandateImportEntryLinks()
  {
    MandateImport = mandateImport.Id
  }
};

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

POST https://api.gocardless.com/mandate_import_entries HTTP/1.1
{
  "mandate_import_entries": {
    "links": {
      "mandate_import": "IM000010790WX1"
    },
    "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": {
      "original_mandate_reference": "REFNMANDATE",
      "original_creditor_id": "FR123OTHERBANK",
      "original_creditor_name": "Amphitryon"
    }
  }
}

HTTP/1.1 201 (Created)
{
  "mandate_import_entries": {
    "record_identifier": "bank-file.xml/line-1",
    "created_at": "2018-03-03T00:00:00Z",
    "links": {
      "mandate_import": "IM000010790WX1"
    }
  }
}

If you provide invalid data, a descriptive error will be returned so you can make corrections.

Submitting the mandate import for review

When all entries have been added to the mandate import, it is time to submit it for review.

In order to defend against possible fraudulent or harmful use of this feature, all submitted imports are subject to review by GoCardless staff. We aim to complete this as quickly as possible.

$client->mandateImports()->submit($mandateImport->id);
client.mandate_imports.submit(mandate_import.id)
@client.mandate_imports.submit(mandate_import.id)
client.mandateImports().submit(mandateImport.getId()).execute();
await gocardless.MandateImports.SubmitAsync(mandateImport.Id);
POST https://api.gocardless.com/mandate_imports/IM000010790WX1/actions/submit HTTP/1.1

HTTP/1.1 200 (OK)
{
  "mandate_imports": {
    "id": "IM000010790WX1",
    "scheme": "bacs",
    "status": "submitted",
    "created_at": "2018-03-12T14:03:04.000Z"
  }
}

Linking up resources

After approval, the mandate import will be processed. As mandates are migrated, you will receive webhooks, just as if they had been created using the mandates API.

If you need to reconcile the new resources with your system, periodically check your mandate import. When its status is processed, listing the mandate import entries will give you the identifiers for the customer, bank account and mandate resources that were created for each mandate import entry.

$mandateImport = $client->mandateImports()->get(mandateImport->id);

if ($mandateImport->status === "processed") {
    doReconciliation();
}

mandate_import = client.mandate_imports.get(mandate_import.id)

if mandate_import.status == "processed":
    do_reconciliation()
mandate_import = @client.mandate_imports.get(mandate_import.id)

if mandate_import.status == "processed"
  do_reconciliation
end
MandateImport mandateImport = client
    .mandateImports()
    .get(mandateImport.getId())
    .execute();

if (mandateImport.getStatus() == "processed") {
    doReconciliation();
}
var response = await gocardless.MandateImports.GetAsync(mandateImport.Id);
GoCardless.Resources.MandateImport import = response.MandateImport;

if (import.Status == MandateImportStatus.Processed)
{
    DoReconciliation();
}
GET https://api.gocardless.com/mandate_imports/IM000010790WX1 HTTP/1.1

HTTP/1.1 200 (OK)
{
  "mandate_imports": {
    "id": "IM000010790WX1",
    "scheme": "bacs",
    "status": "processed",
    "created_at": "2018-03-12T14:03:04.000Z"
  }
}
$entries = $client->mandateImportEntries()->all([
  "params" => ["mandate_import" => "IM000010790WX1"]
]);

foreach ($entries as $i => $entry) {
  echo $entry->recordIdentifier;
  echo $entry->links['customer'];
  echo $entry->links['customer_bank_account'];
  echo $entry->links['mandate'];
}
response = client.mandate_import_entries.all(
  params={ "mandate_import": mandate_import.id }
).records

for entry in records:
    print(entry.record_identifier)
    print(entry.links.customer)
    print(entry.links.customer_bank_account)
    print(entry.links.mandate)
@client.mandate_import_entries.all(
  params: {
    "mandate_import" => mandate_import.id
  }
).each do |entry|
  puts entry.record_identifier
  puts entry.links.customer
  puts entry.links.customer_bank_account
  puts entry.links.mandate
end

for (MandateImportEntry entry : client.mandateImportEntries().all().withMandateImport("IM000010790WX1").execute()) {
  System.out.println(entry.getRecordIdentifier());
  System.out.println(entry.getLinks().getCustomer());
  System.out.println(entry.getLinks().getCustomerBankAccount());
  System.out.println(entry.getLinks().getMandate());
}
var request = new GoCardless.Services.MandateImportEntryListRequest()
{
    MandateImport = mandateImport.Id
};

var response = gocardless.MandateImportEntries.All(request);
foreach (GoCardless.Resources.MandateImportEntry entry in response)
{
    Console.WriteLine(entry.RecordIdentifier);
    Console.WriteLine(entry.Links.Customer);
    Console.WriteLine(entry.Links.CustomerBankAccount);
    Console.WriteLine(entry.Links.Mandate);
}
GET https://api.gocardless.com/mandate_import_entries?mandate_import=IM000010790WX1 HTTP/1.1

HTTP/1.1 200 (OK)
{
  "mandate_import_entries": [
    {
      "record_identifier": "bank-file.xml/line-2",
      "created_at": "2018-03-03T00:00:01Z",
      "links": {
        "mandate_import": "IM000010790WX1",
        "customer": "CU456",
        "customer_bank_account": "BA456",
        "mandate": "MD456"
      }
    },
    {
      "record_identifier": "bank-file.xml/line-1",
      "created_at": "2018-03-03T00:00:00Z",
      "links": {
        "mandate_import": "IM000010790WX1",
        "customer": "CU123",
        "customer_bank_account": "BA123",
        "mandate": "MD123"
      }
    }
  ],
  "meta": {
    "cursors": {
      "before": null,
      "after": null
    },
    "limit": 50
  }
}
If, having created a mandate import, you need to cancel it for any reason, use the provided cancellation API. Please note that once a submitted mandate import has been approved by our team, it can no longer be cancelled or reversed.