TypeScript contact list API upload stalls on chunked multipart streaming with deduplication hash collisions

Problem

We’re building a TypeScript reconciler to push outbound contact lists from our CRM into Genesys Cloud. Large payloads are failing during the streaming multipart phase. The flow handles delta encoding, runs field mapping validations against schema constraints, and batches updates. The deduplication logic uses a SHA-256 hash on the email and phone fields to skip repeats. We also log each batch to S3 for governance compliance before pushing. The script needs to track upload latency and flag errors for our data quality dashboard. The API keeps rejecting the chunked transfer requests though.

Code

Here’s the upload loop handling the streaming payload and hash check:

const generateHash = (contact: Contact) => crypto.createHash('sha256').update(`${contact.email}|${contact.phone}`).digest('hex');

const stream = new ReadableStream({
 start(controller) {
 for (const contact of batch) {
 if (seenHashes.has(generateHash(contact))) continue;
 const payload = JSON.stringify({
 listId: targetListId,
 fields: { phone: contact.phone, email: contact.email, gdpr_consent: contact.consent }
 });
 controller.enqueue(new TextEncoder().encode(payload));
 }
 controller.close();
 }
});

await fetch(`${GC_API}/api/v2/outbound/contactslists/${listId}/contacts`, {
 method: 'POST',
 headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
 body: stream,
 duplex: 'half'
});

Error

The request hangs for about forty seconds before throwing a 408 Request Timeout. The response payload doesn’t show validation failures, just a generic gateway error.

{
 "message": "Request timeout exceeded during chunked upload processing",
 "code": "408",
 "status": "Request Timeout"
}

Question

Headers are a mess sometimes. The chunked transfer encoding seems to choke on the stream controller. Is there a specific header or boundary format required for the outbound contacts endpoint when pushing large batches? The docs mention multipart but don’t clarify how to structure the JSON chunks with the deduplication hash. We’ve tried adjusting the fetch duplex settings without luck. The latency metrics are spiking right before the timeout.

Problem The streaming multipart boundary handling gets tripped up when chunk sizes exceed the platform’s expected limits. The contact management API drops payloads if the deduplication hash collides with an existing record in the same batch, causing the stream to hang instead of returning a proper conflict error. Try adjusting the fetch logic to enforce a fixed chunk size and strip the collision hashes before sending. The server really doesn’t like overlapping boundaries. It’ll just freeze the connection.

const CHUNK_LIMIT = 500;
const cleanBatch = batch.filter(c => !seenHashes.has(c.dedupHash));
seenHashes.add(c.dedupHash);
const formData = new FormData();
formData.append('contacts', JSON.stringify(cleanBatch.slice(0, CHUNK_LIMIT)));
await fetch(`${API_BASE}/contact-lists/${listId}/upload`, {
 method: 'POST',
 body: formData,
 headers: { 'Authorization': `Bearer ${token}` }
});

Error The platform buffers the entire chunk before validating the dedup key. If the hash matches a record already queued in that same batch, the buffer never flushes. Did the payload validator flag the boundary mismatch? You’ll see the connection stall right at the final boundary marker. Double check the hash generation logic against the API’s expected field order. The contact API expects E.164 formatting before the hash runs. Strip the country code prefix before generating the SHA-256 digest.