{"slug": "build-an-execution-receipt-wrapper-for-llm-api-calls", "title": "Build an Execution Receipt Wrapper for LLM API Calls", "summary": "A developer proposes an execution receipt wrapper for LLM API calls that preserves operational metadata without logging full prompts or responses. The receipt includes fields like request ID, latency, validation outcome, and fallback status, enabling investigation of production responses while minimizing data exposure. The approach uses an append-oriented, versioned store separate from general-purpose logs.", "body_md": "Model integrations are easy to instrument badly.\n\nLogging every prompt creates unnecessary data exposure. Logging only the model name leaves too little evidence when a production response needs investigation.\n\nAn execution receipt offers a middle path: preserve operational metadata without copying the full interaction into a general-purpose log.\n\nWhat belongs in the receipt?\n\nUseful fields include:\n\nrequest and task identifiers;\n\nprompt, policy, and schema versions;\n\nendpoint alias;\n\nstart time and latency;\n\nusage measurements;\n\nvalidation outcome;\n\nfallback or retry status.\n\nApplication request\n\n↓\n\nRedaction and classification\n\n↓\n\nModel endpoint\n\n↓\n\nOutput validation\n\n↓\n\nExecution receipt store\n\nTeams evaluating model endpoints for this architecture can place VectorEngine behind the same application-owned wrapper and receipt format.\n\nDisclosure: this article includes an external referral link for readers who want to explore the platform.\n\nTypeScript-style pseudocode\n\ntype Receipt = {\n\nrequestId: string;\n\ntask: string;\n\npromptVersion: string;\n\nendpointAlias: string;\n\nstartedAt: string;\n\nlatencyMs?: number;\n\ncontractPassed?: boolean;\n\nfallbackUsed: boolean;\n\nstatus: \"started\" | \"completed\" | \"failed\";\n\n};\n\nasync function invokeWithReceipt(input: unknown, ctx: Context) {\n\nconst started = Date.now();\n\nconst receipt: Receipt = {\n\nrequestId: ctx.requestId,\n\ntask: ctx.task,\n\npromptVersion: ctx.promptVersion,\n\nendpointAlias: ctx.endpointAlias,\n\nstartedAt: new Date(started).toISOString(),\n\nfallbackUsed: false,\n\nstatus: \"started\"\n\n};\n\ntry {\n\nconst result = await invokeEndpoint(input, ctx.endpointAlias);\n\n```\nreceipt.latencyMs = Date.now() - started;\nreceipt.contractPassed = validateOutput(result);\nreceipt.status = \"completed\";\n\nawait appendReceipt(receipt);\nreturn result;\n```\n\n} catch (error) {\n\nreceipt.latencyMs = Date.now() - started;\n\nreceipt.status = \"failed\";\n\n```\nawait appendReceipt(receipt);\nthrow error;\n```\n\n}\n\n}\n\nThis example intentionally excludes raw input and output. If investigation requires content access, store it under a separate retention and authorization policy rather than embedding it in every receipt.\n\nReceipts should also be append-oriented and versioned. Editing old records in place makes later reconstruction harder.\n\nThe goal is modest: retain enough evidence to understand how a model-backed feature executed, while collecting no more content than the workflow requires.", "url": "https://wpnews.pro/news/build-an-execution-receipt-wrapper-for-llm-api-calls", "canonical_source": "https://dev.to/_9de8b28cd0a409b80cfdc/build-an-execution-receipt-wrapper-for-llm-api-calls-2nji", "published_at": "2026-07-16 06:00:38+00:00", "updated_at": "2026-07-16 06:06:20.425546+00:00", "lang": "en", "topics": ["large-language-models", "ai-infrastructure", "developer-tools"], "entities": ["VectorEngine"], "alternates": {"html": "https://wpnews.pro/news/build-an-execution-receipt-wrapper-for-llm-api-calls", "markdown": "https://wpnews.pro/news/build-an-execution-receipt-wrapper-for-llm-api-calls.md", "text": "https://wpnews.pro/news/build-an-execution-receipt-wrapper-for-llm-api-calls.txt", "jsonld": "https://wpnews.pro/news/build-an-execution-receipt-wrapper-for-llm-api-calls.jsonld"}}