fix(documenso): skip legacy v1 document IDs in the v2 status poller
All checks were successful
Build & Push Docker Images / lint (push) Successful in 3m7s
Build & Push Docker Images / build-and-push (push) Successful in 9m22s

The signature-poll worker fetched every sent/partially_signed document via
GET /api/v2/envelope/{id}. Pre-cutover documents carry a bare numeric
documenso_id (e.g. "46"), which the v2 envelope endpoint rejects with 400
"Invalid envelope ID" — ~36 errors/hour on prod from 3 abandoned June-3 EOIs.

Skip documents whose documenso_id isn't an `envelope_xxx` string when the
port is on the v2 API (v1-API ports keep polling numeric ids). New v2 signing
is unaffected. Pure isPollableDocumensoId() helper, unit-tested.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2qc3xZTfif7N4Wq3QDa8X
This commit is contained in:
2026-06-28 18:48:20 +02:00
parent 32b57354ad
commit ba128646e1
2 changed files with 61 additions and 0 deletions

View File

@@ -9,8 +9,25 @@ import {
handleDocumentExpired,
handleDocumentRejected,
} from '@/lib/services/documents.service';
import { getPortDocumensoConfig, type DocumensoApiVersion } from '@/lib/services/port-config';
import { logger } from '@/lib/logger';
/**
* Whether a document's `documensoId` can be fetched under the port's current
* Documenso API version. A v2 envelope id is the public `envelope_xxx` string;
* a legacy v1 id is a bare numeric (e.g. "46"). The v2 `/api/v2/envelope/{id}`
* endpoint rejects numerics with a 400 "Invalid envelope ID", so a port that
* has cut over to v2 must SKIP its old v1 documents in the poll instead of
* erroring on every cycle. Ports still on the v1 API keep polling numeric ids.
*/
export function isPollableDocumensoId(
documensoId: string,
apiVersion: DocumensoApiVersion,
): boolean {
if (apiVersion === 'v2' && !documensoId.startsWith('envelope_')) return false;
return true;
}
export async function processDocumensoPoll(): Promise<void> {
// Find all documents that are in-progress signing and have a documensoId
const pendingDocs = await db.query.documents.findMany({
@@ -24,9 +41,25 @@ export async function processDocumensoPoll(): Promise<void> {
logger.info({ count: pendingDocs.length }, 'Polling Documenso for document statuses');
// Per-port API version, resolved once per port (cheap memo for the run).
const apiVersionByPort = new Map<string, DocumensoApiVersion>();
let skippedLegacy = 0;
for (const doc of pendingDocs) {
if (!doc.documensoId) continue;
let apiVersion = apiVersionByPort.get(doc.portId);
if (!apiVersion) {
apiVersion = (await getPortDocumensoConfig(doc.portId)).apiVersion;
apiVersionByPort.set(doc.portId, apiVersion);
}
// Skip legacy v1 documents the port's v2 API can't resolve — otherwise each
// poll cycle 400s ("Invalid envelope ID") on abandoned pre-cutover docs.
if (!isPollableDocumensoId(doc.documensoId, apiVersion)) {
skippedLegacy++;
continue;
}
try {
// Pass the doc's portId so the client uses per-port credentials
// (admin-set Documenso URL/key/version), not the global env fallback.
@@ -112,4 +145,11 @@ export async function processDocumensoPoll(): Promise<void> {
);
}
}
if (skippedLegacy > 0) {
logger.info(
{ skippedLegacy },
'Documenso poll: skipped legacy v1 documents not resolvable on the v2 API',
);
}
}