Skip to main content
documenso-sdk-patterns Apply production-ready Documenso SDK patterns for TypeScript and Python.
Use when implementing Documenso integrations, refactoring SDK usage,
or establishing team coding standards for Documenso.
Trigger with phrases like "documenso SDK patterns", "documenso best practices",
"documenso code patterns", "idiomatic documenso".
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill documenso-sdk-patterns명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... jeremylongshore
jeremylongshore/claude-code-plugins-plus-skills
GitHub 저장소 열기 name documenso-sdk-patterns description Apply production-ready Documenso SDK patterns for TypeScript and Python.
Use when implementing Documenso integrations, refactoring SDK usage,
or establishing team coding standards for Documenso.
Trigger with phrases like "documenso SDK patterns", "documenso best practices",
"documenso code patterns", "idiomatic documenso".
allowed-tools Read, Write, Edit version 1.13.0 license MIT author Jeremy Longshore <jeremy@intentsolutions.io> tags ["saas","documenso","python","typescript"] compatibility Designed for Claude Code, also compatible with Codex and OpenClaw
Documenso SDK Patterns
Overview
Production-ready patterns for the Documenso TypeScript SDK (@documenso/sdk-typescript) and Python SDK. Covers singleton clients, typed wrappers, error handling, retry logic, and testing patterns.
Prerequisites
Completed documenso-install-auth setup
Familiarity with async/await and TypeScript generics
Understanding of error handling best practices
Instructions
Pattern 1: Singleton Client with Configuration
import { Documenso } from "@documenso/sdk-typescript" ;
interface DocumensoConfig {
apiKey : string ;
baseUrl ?: string ;
timeout ?: number ;
}
let instance : Documenso | null = null ;
export function getDocumensoClient (config ?: DocumensoConfig ): Documenso {
if (!instance) {
const apiKey = config?.apiKey ?? process.env .DOCUMENSO_API_KEY ;
if (!apiKey) throw new Error ("DOCUMENSO_API_KEY is required" );
instance = new Documenso ({
apiKey,
...(config?.baseUrl && { serverURL : config. }),
});
}
instance;
}
( ): {
instance = ;
}
baseUrl
return
export
function
resetClient
void
null
Pattern 2: Typed Document Service
import { getDocumensoClient } from "./client" ;
export interface CreateDocumentInput {
title : string ;
pdfPath : string ;
signers : Array <{
email : string ;
name : string ;
fields : Array <{
type : "SIGNATURE" | "INITIALS" | "NAME" | "EMAIL" | "DATE" | "TEXT" ;
pageNumber : number ;
pageX : number ;
pageY : number ;
pageWidth ?: number ;
pageHeight ?: number ;
}>;
}>;
}
export interface DocumentResult {
documentId : number ;
recipientIds : number [];
status : "DRAFT" | "PENDING" | "COMPLETED" ;
}
export async function createAndSendDocument (
input : CreateDocumentInput
): Promise <DocumentResult > {
const client = getDocumensoClient ();
const { readFileSync } = await import ("fs" );
const doc = await client.documents .createV0 ({ title : input.title });
const pdfBuffer = readFileSync (input.pdfPath );
await client.documents .setFileV0 (doc.documentId , {
file : new Blob ([pdfBuffer], { type : "application/pdf" }),
});
const recipientIds : number [] = [];
for (const signer of input.signers ) {
const recipient = await client.documentsRecipients .createV0 (doc.documentId , {
email : signer.email ,
name : signer.name ,
role : "SIGNER" ,
});
recipientIds.push (recipient.recipientId );
for (const field of signer.fields ) {
await client.documentsFields .createV0 (doc.documentId , {
recipientId : recipient.recipientId ,
type : field.type ,
pageNumber : field.pageNumber ,
pageX : field.pageX ,
pageY : field.pageY ,
pageWidth : field.pageWidth ?? 20 ,
pageHeight : field.pageHeight ?? 5 ,
});
}
}
await client.documents .sendV0 (doc.documentId );
return { documentId : doc.documentId , recipientIds, status : "PENDING" };
}
Pattern 3: Error Handling Wrapper
export class DocumensoError extends Error {
constructor (
message : string ,
public statusCode ?: number ,
public retryable : boolean = false
) {
super (message);
this .name = "DocumensoError" ;
}
}
export async function withErrorHandling<T>(
operation : string ,
fn : () => Promise <T>
): Promise <T> {
try {
return await fn ();
} catch (err : any ) {
const status = err.statusCode ?? err.status ;
switch (status) {
case 401 :
throw new DocumensoError (`${operation} : Invalid API key` , 401 , false );
case 403 :
throw new DocumensoError (
`${operation} : Insufficient permissions — use team API key` ,
403 ,
false
);
case 404 :
throw new DocumensoError (`${operation} : Resource not found` , 404 , false );
case 429 :
throw new DocumensoError (`${operation} : Rate limited` , 429 , true );
case 500 :
case 502 :
case 503 :
throw new DocumensoError (
`${operation} : Documenso server error` ,
status,
true
);
default :
throw new DocumensoError (
`${operation} : ${err.message ?? "Unknown error" } ` ,
status,
false
);
}
}
}
Pattern 4: Retry with Exponential Backoff
import { DocumensoError } from "./errors" ;
interface RetryConfig {
maxRetries : number ;
baseDelayMs : number ;
maxDelayMs : number ;
}
const DEFAULT_RETRY : RetryConfig = {
maxRetries : 3 ,
baseDelayMs : 1000 ,
maxDelayMs : 30000 ,
};
export async function withRetry<T>(
fn : () => Promise <T>,
config : Partial <RetryConfig > = {}
): Promise <T> {
const { maxRetries, baseDelayMs, maxDelayMs } = { ...DEFAULT_RETRY , ...config };
let lastError : Error | undefined ;
for (let attempt = 0 ; attempt <= maxRetries; attempt++) {
try {
return await fn ();
} catch (err) {
lastError = err as Error ;
if (err instanceof DocumensoError && !err.retryable ) throw err;
if (attempt === maxRetries) break ;
const delay = Math .min (baseDelayMs * 2 ** attempt, maxDelayMs);
const jitter = delay * (0.5 + Math .random () * 0.5 );
await new Promise ((r ) => setTimeout (r, jitter));
}
}
throw lastError;
}
Pattern 5: Python Service Pattern
from documenso_sdk_python import Documenso
from dataclasses import dataclass
from typing import Optional
import os
@dataclass
class SignerInput :
email: str
name: str
field_type: str = "SIGNATURE"
page: int = 1
x: float = 50.0
y: float = 80.0
class DocumensoService :
def __init__ (self, api_key: Optional [str ] = None , base_url: Optional [str ] = None ):
self .client = Documenso(
api_key=api_key or os.environ["DOCUMENSO_API_KEY" ],
**({"server_url" : base_url} if base_url else {}),
)
def create_and_send (
self, title: str , pdf_path: str , signers: list [SignerInput]
) -> dict :
doc = self .client.documents.create_v0(title=title)
with open (pdf_path, "rb" ) as f:
self .client.documents.set_file_v0(doc.document_id, file=f.read())
recipient_ids = []
for signer in signers:
recip = self .client.documents_recipients.create_v0(
doc.document_id, email=signer.email, name=signer.name, role="SIGNER"
)
recipient_ids.append(recip.recipient_id)
self .client.documents_fields.create_v0(
doc.document_id,
recipient_id=recip.recipient_id,
type =signer.field_type,
page_number=signer.page,
page_x=signer.x,
page_y=signer.y,
)
self .client.documents.send_v0(doc.document_id)
return {"document_id" : doc.document_id, "recipient_ids" : recipient_ids}
Pattern 6: Testing with Mocks
import { vi } from "vitest" ;
export function createMockClient ( ) {
return {
documents : {
createV0 : vi.fn ().mockResolvedValue ({ documentId : 1 }),
setFileV0 : vi.fn ().mockResolvedValue (undefined ),
findV0 : vi.fn ().mockResolvedValue ({ documents : [] }),
sendV0 : vi.fn ().mockResolvedValue (undefined ),
deleteV0 : vi.fn ().mockResolvedValue (undefined ),
},
documentsRecipients : {
createV0 : vi.fn ().mockResolvedValue ({ recipientId : 100 }),
},
documentsFields : {
createV0 : vi.fn ().mockResolvedValue ({ fieldId : 200 }),
},
};
}
Error Handling Pattern Issue Cause Solution Client not initialized Missing env var Check DOCUMENSO_API_KEY is set Singleton stale after key rotation Cached client Call resetClient() Retry loop on 401 Non-retryable treated as retryable Check retryable flag Type mismatch on field type Wrong enum string Use union type from SDK
Resources
Next Steps Apply patterns in documenso-core-workflow-a for document creation workflows.