Your duplicate payment issue stems from how Workday’s API handles duplicate detection versus UI validation. Here’s the comprehensive solution:
Supplier Invoice API Behavior:
The REST API in R2-2023 uses a multi-layered approach to duplicate prevention:
- Request-Level Deduplication (Idempotency)
- Business-Level Duplicate Detection (Invoice Number Validation)
- Race Condition Protection (Database Constraints)
Your current implementation only addresses layer 1, which is insufficient.
Duplicate Detection Logic:
Workday’s duplicate validation checks three criteria:
- Supplier ID match
- Invoice number match
- Invoice date within tolerance window (default 30 days)
However, this validation must be explicitly requested in the API call:
POST /supplierInvoices
X-Idempotency-Key: unique-request-id-12345
Content-Type: application/json
{
"supplierId": "SUP-1001",
"invoiceNumber": "INV-2025-0603",
"invoiceDate": "2025-06-01",
"amount": 5000.00,
"currency": "USD",
"validationOptions": {
"checkDuplicates": true,
"duplicateWindow": 30,
"strictMatching": true
}
}
Key additions:
- checkDuplicates: Enables business-level duplicate validation
- duplicateWindow: Days to search for existing invoices (default 30)
- strictMatching: Requires exact invoice number match (vs fuzzy matching)
Without the validationOptions object, the API assumes you’re handling duplicate detection client-side and will create the invoice regardless of existing records.
Response for Duplicate Detection:
When a duplicate is detected:
HTTP 409 Conflict
{
"error": "DUPLICATE_INVOICE",
"message": "Invoice INV-2025-0603 already exists for supplier SUP-1001",
"existingInvoice": {
"invoiceId": "SI-2025-06-001",
"status": "APPROVED",
"createdDate": "2025-06-01T10:30:00Z"
},
"validationFailures": [
{
"field": "invoiceNumber",
"reason": "DUPLICATE_DETECTED",
"existingValue": "INV-2025-0603"
}
]
}
This response allows your application to handle duplicates appropriately (log, alert, skip, etc.).
Client-Side Validation:
While API-level validation is essential, implement client-side checking as a first line of defense:
// Pseudocode - Pre-submission validation:
1. Before posting invoice, query existing invoices:
GET /supplierInvoices?supplier=SUP-1001&invoiceNumber=INV-2025-0603
2. If results returned:
a. Check invoice status (DRAFT, PENDING, APPROVED, PAID)
b. If status is DRAFT, update existing instead of creating new
c. If status is PENDING/APPROVED/PAID, reject as duplicate
3. If no results, proceed with POST request
Include validationOptions with checkDuplicates=true
4. Handle 409 Conflict response:
Log duplicate attempt with existing invoice reference
Do not retry - mark as duplicate in source system
This two-phase approach (client check + API validation) provides robust duplicate prevention.
Idempotency Key Strategy:
Idempotency keys prevent duplicate submissions of the same request but don’t detect business-level duplicates:
Idempotency Key Format:
{source_system}:{supplier_id}:{invoice_number}:{timestamp}
Example:
ERP_SYSTEM_A:SUP-1001:INV-2025-0603:1717416000
Use a deterministic key based on invoice attributes, not a random UUID. This ensures:
- Same invoice from same source generates same key
- Different sources for same invoice generate different keys (allowing detection)
- Timestamp component handles legitimate resubmissions after corrections
Configuration Requirements:
Enable API duplicate detection in tenant configuration:
Workday Admin:
Setup > Accounts Payable > Supplier Invoice Settings
API Configuration:
☑ Enable API Duplicate Detection
☑ Enforce Supplier + Invoice Number Uniqueness
☑ Apply Date Window for Duplicate Search
Window: 30 days (configurable 1-90)
☑ Return Detailed Validation Errors
Exception Handling:
☐ Allow Override with Justification Code
(Uncheck - prevents API from bypassing validation)
Without these settings enabled, the validationOptions in your API request will be ignored.
Race Condition Prevention:
For high-volume integrations with multiple concurrent processes, implement application-level locking:
// Pseudocode - Distributed lock pattern:
1. Acquire lock: LOCK key="invoice:{supplier}:{invoiceNum}" timeout=30s
2. If lock acquired:
a. Query for existing invoice
b. If not exists, POST new invoice with validation
c. Store invoice ID in cache with lock key
d. Release lock
3. If lock not acquired:
a. Wait for lock release (max 30s)
b. Check cache for invoice ID from other process
c. If found, return existing invoice reference
d. If not found, retry lock acquisition
This prevents the scenario where two API calls pass duplicate validation simultaneously because neither invoice exists in the database yet.
Testing Duplicate Detection:
Validate your implementation:
Test Scenario 1 - Same Request Twice:
1. POST invoice with idempotency key "KEY-001"
2. POST same invoice with same key "KEY-001"
Expected: Second request returns original response, no duplicate created
Test Scenario 2 - Same Invoice Different Key:
1. POST invoice with idempotency key "KEY-001"
2. POST same invoice details with different key "KEY-002" and checkDuplicates=true
Expected: Second request returns 409 Conflict
Test Scenario 3 - Concurrent Submissions:
1. POST invoice from Process A
2. Simultaneously POST same invoice from Process B
Expected: One succeeds, one returns 409 Conflict (with application locking)
All three scenarios must pass to ensure comprehensive duplicate prevention.
Implementing these layers - API validation options, client-side checking, proper idempotency keys, and application locking - will eliminate duplicate invoice creation and prevent double payments in your AP automation.
This draft is based on general Workday knowledge. It has not been verified against your specific version and environment. Practitioners: verify the steps and share your experience below.