POST
/
external-api
/
v1
/
debts
/
batch
Batch Create Debts
curl --request POST \
  --url https://getbill.io/external-api/v1/debts/batch \
  --header 'Authorization: <authorization>' \
  --header 'Content-Type: <content-type>' \
  --data '
{
  "batch_name": "<string>",
  "accept_expensive_destination": true,
  "debts": [
    {
      "firstname": "<string>",
      "lastname": "<string>",
      "civility": "<string>",
      "phone": "<string>",
      "amount": 123,
      "currency": "<string>",
      "email": "<string>",
      "birthdate": "<string>",
      "object": "<string>",
      "internal_id": "<string>",
      "invoice_date": "<string>",
      "due_date": "<string>",
      "address": "<string>",
      "iban": "<string>",
      "company": "<string>",
      "debtor_company": "<string>",
      "street_address": "<string>",
      "street_number": "<string>",
      "postal_code": "<string>",
      "city": "<string>",
      "country": "<string>",
      "payment_link": "<string>",
      "timeline_id": "<string>",
      "timeline_start_mode": "<string>",
      "metadata": {},
      "temp_id": "<string>",
      "accept_expensive_destination": true
    }
  ]
}
'
{
  "error": false,
  "message": "Batch operation completed",
  "data": {
    "import_id": "enc_imp_abc123xyz",
    "created": [
      {
        "index": 0,
        "temp_id": "import-001",
        "debt_id": "abc123def456",
        "status": "valid",
        "data": {
          "firstname": "John",
          "lastname": "Doe",
          "phone": "+33123456789",
          "email": "john.doe@example.com",
          "amount": 1250.00,
          "currency": "EUR"
        }
      },
      {
        "index": 1,
        "temp_id": "import-002",
        "debt_id": "ghi789jkl012",
        "status": "valid",
        "data": {
          "firstname": "Jane",
          "lastname": "Smith",
          "phone": "+33987654321",
          "email": "jane.smith@example.com",
          "amount": 750.00,
          "currency": "EUR"
        }
      }
    ],
    "failed": [],
    "skipped": [],
    "summary": {
      "total": 2,
      "created": 2,
      "failed": 0,
      "skipped": 0,
      "valid": 2,
      "error": 0
    }
  }
}

Overview

This endpoint allows you to create multiple debt records in a single API call. It’s designed for bulk imports and can process up to 1,000 debts per request. Each debt is validated individually, and the endpoint returns both successful creations and any failures.

Authentication

Requires a valid OAuth 2.0 access token with the debts:write scope.

Request

Authorization
string
required
Bearer token for authentication
Content-Type
string
required
Must be application/json

Request Body

batch_name
string
Custom name for this import batch (optional). Appears in the dashboard import filter. Default: “API Batch - DD/MM/YYYY HH:mm”
accept_expensive_destination
boolean
Batch-level opt-in for expensive destinations (DOM-TOM, Maghreb, etc.). When set to true, all debts in the batch with expensive destination phone numbers will be accepted. Can also be set per-debt inside the debts array. If not set, debts with expensive destination phones will fail validation with a specific error.
debts
array
required
Array of debt objects to create. Maximum 1,000 debts per request. Each debt object has the same structure as the Create Debt endpoint.

Example Request

curl -X POST "/external-api/v1/debts/batch" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "batch_name": "January 2025 Invoices",
    "debts": [
      {
        "temp_id": "import-001",
        "firstname": "John",
        "lastname": "Doe",
        "civility": "Mr",
        "phone": "+33123456789",
        "email": "john.doe@example.com",
        "amount": 1250.00,
        "currency": "EUR",
        "object": "Invoice #INV-2024-001",
        "internal_id": "DEBT-2024-001",
        "due_date": "2023-12-31",
        "timeline_id": "xyz789abc123",
        "timeline_start_mode": "next_day",
        "metadata": {"crm_id": "CRM-001", "source": "batch_import"}
      },
      {
        "temp_id": "import-002",
        "firstname": "Jane",
        "lastname": "Smith",
        "civility": "Ms",
        "phone": "+33987654321",
        "email": "jane.smith@example.com",
        "amount": 750.00,
        "currency": "EUR",
        "object": "Invoice #INV-2024-002",
        "internal_id": "DEBT-2024-002",
        "due_date": "2023-12-31",
        "timeline_id": "xyz789abc123",
        "timeline_start_mode": "immediate",
        "metadata": {"crm_id": "CRM-002", "source": "batch_import"}
      }
    ]
  }'

Response

error
boolean
Always false for successful batch requests (even if some individual debts failed)
message
string
Success message
data
object
Batch operation results

Success Response

{
  "error": false,
  "message": "Batch operation completed",
  "data": {
    "import_id": "enc_imp_abc123xyz",
    "created": [
      {
        "index": 0,
        "temp_id": "import-001",
        "debt_id": "abc123def456",
        "status": "valid",
        "data": {
          "firstname": "John",
          "lastname": "Doe",
          "phone": "+33123456789",
          "email": "john.doe@example.com",
          "amount": 1250.00,
          "currency": "EUR"
        }
      },
      {
        "index": 1,
        "temp_id": "import-002",
        "debt_id": "ghi789jkl012",
        "status": "valid",
        "data": {
          "firstname": "Jane",
          "lastname": "Smith",
          "phone": "+33987654321",
          "email": "jane.smith@example.com",
          "amount": 750.00,
          "currency": "EUR"
        }
      }
    ],
    "failed": [],
    "skipped": [],
    "summary": {
      "total": 2,
      "created": 2,
      "failed": 0,
      "skipped": 0,
      "valid": 2,
      "error": 0
    }
  }
}

Partial Success Response

{
  "error": false,
  "message": "Batch operation completed",
  "data": {
    "import_id": "enc_imp_def456ghi",
    "created": [
      {
        "index": 0,
        "temp_id": "import-001",
        "debt_id": "abc123def456",
        "status": "valid",
        "data": {
          "firstname": "John",
          "lastname": "Doe",
          "phone": "+33123456789",
          "email": "john.doe@example.com",
          "amount": 1250.00,
          "currency": "EUR"
        }
      }
    ],
    "failed": [
      {
        "index": 1,
        "status": "error",
        "errors": {
          "phone": "Invalid phone number",
          "contact": "At least one valid contact method (email or phone) is required"
        },
        "data": {
          "firstname": "Jane",
          "lastname": "Smith",
          "phone": "0690123456",
          "email": "invalid-email",
          "amount": 750.00,
          "currency": "EUR"
        }
      },
      {
        "index": 3,
        "status": "error",
        "errors": {
          "firstname": "First name is required",
          "lastname": "Last name is required",
          "amount": "Valid amount (greater than 0) is required"
        },
        "data": {
          "phone": "+33123456789",
          "amount": 0,
          "currency": "EUR"
        }
      },
      {
        "index": 2,
        "errors": [
          "Invalid phone number: \"12345\" - phone number too short"
        ]
      },
      {
        "index": 4,
        "status": "error",
        "errors": {
          "phone": "Expensive destination: Réunion (RE) — 100 credits per AI call. Add \"accept_expensive_destination\": true to proceed."
        },
        "data": {
          "firstname": "Marc",
          "lastname": "Dupont",
          "phone": "+262692123456",
          "amount": 500.00,
          "currency": "EUR"
        }
      }
    ],
    "skipped": [
      {
        "index": 2,
        "reason": "Empty or instruction row",
        "data": {}
      }
    ],
    "summary": {
      "total": 4,
      "created": 1,
      "failed": 2,
      "skipped": 1,
      "valid": 1,
      "error": 2
    }
  }
}

Error Responses

{
  "error": true,
  "message": "No debts provided in payload",
  "code": 400
}

Validation Rules

Validation Update (2025): Phone number validation is now strictly enforced to match the web interface behavior. Phone numbers are validated using international standards (libphonenumber library). If you were previously sending phone numbers without proper formatting or French overseas mobile numbers (069X prefix), these will now be rejected. You can now use email as an alternative to phone.
  • Maximum 1,000 debts per batch request
  • Each debt is validated individually
  • Failed debts don’t stop processing of successful ones
Each debt is validated using the same rules as the web import interface:Required Fields:
  • firstname: Debtor’s first name (required)
  • lastname: Debtor’s last name (required)
  • amount: Must be a positive number (> 0)
  • currency: Required, must be valid ISO 4217 code
Contact Information (at least one required):
  • phone OR email: At least one must be valid
  • phone: Must be a valid phone number. International format recommended (e.g., +33123456789)
    • Unsupported: French overseas mobile numbers with 069X prefix (e.g., 0690123456)
    • Supported: French mainland, most international numbers, French overseas fixed lines
  • email: Must be a valid email format
Optional Fields:
  • Date fields: Must be in YYYY-MM-DD format if provided
  • phone: Validated and normalized to E.164 format. French numbers without country code are automatically normalized. Invalid phones are rejected with specific error messages.
  • All other fields are optional
Automatic Skipping:
  • Empty rows are automatically skipped
  • Instruction rows (starting with *, containing “champs obligatoires”, etc.) are skipped
Phone numbers in certain regions cost significantly more credits for AI calls. Debts with expensive destination phones will fail validation unless you opt in:
  • Batch-level: Set "accept_expensive_destination": true at the top level (next to debts array) to accept all expensive destinations in the batch
  • Per-debt: Set "accept_expensive_destination": true on individual debt objects
DestinationCredits per callMultiplier
France (mainland)251.0x
EU countries301.2x
DOM-TOM (Réunion, Guadeloupe, Martinique, etc.)1004.0x
Maghreb (Morocco, Algeria, Tunisia)1255.0x
Failed debts will include the destination details and cost in the errors field.
  • timeline_id: Must be valid and belong to your company
  • timeline_start_mode: Applies individually to each debt
  • Invalid timeline IDs are silently ignored per debt
  • Public holidays and excluded days are respected for next_day mode

Rate Limiting

This endpoint is subject to rate limiting. See the Rate Limits documentation for details. Rate Limit: 50 requests per hour (batch operations) Note: Each batch request can contain up to 1,000 debts, making this much more efficient than individual creation.

Best Practices

Use temp_id

Always provide a temp_id for each debt to easily match responses to your original data.

Handle Partial Success

Always check created, failed, and skipped arrays. Process successful debts, identify why failures occurred using the detailed errors field, and retry or fix validation issues.

Batch Size Strategy

While you can send 1,000 debts, consider batches of 100-500 for better error handling and progress tracking.

Validate Before Sending

Pre-validate data on your side to minimize API failures. Key checks: phone format (avoid 069X French overseas mobile), email format, positive amounts, required fields.

Use Detailed Error Messages

Each failed debt includes the original data and specific field-level errors. Use this to build error reports for manual correction or automated retry logic.

Timeline Assignment

Assign the same timeline_id to all debts in a batch for consistent AI-powered collection workflows.

Monitor Progress

Use the summary object to track overall success rate and identify issues quickly.

Performance Considerations

  • All debts in a batch are processed in a single database transaction
  • Failed validations don’t affect successful creations
  • Expected processing time: ~100-500ms for 100 debts
  • Timeline calculations (next_day mode) add minimal overhead

After Batch Creation

Once debts are created via batch:
  1. Store Debt IDs: Map returned debt_id values to your temp_id or internal_id
  2. Process Failures: Review the failed array for validation errors. Each failed item includes:
    • Original data (data field) to identify which row failed
    • Detailed field-level errors (errors field) to understand why
    • Common fixes: Update invalid phone numbers (avoid 069X), fix email formats, ensure required fields
  3. Handle Skipped Rows: Check the skipped array for empty or instruction rows that were automatically filtered
  4. Monitor Workflows: If timelines are assigned, collection actions will start automatically for successfully created debts
  5. Track Progress: Use the statistics endpoints to monitor collection progress