@agoodway/goodleads-server
v0.1.0
Published
Server-side session recording flow handlers for GoodLeads. Validates lead sessions, creates and completes session recordings via the GoodAudit API, and manages signed completion cookies. Framework-agnostic — works with any server that supports the Web `Re
Readme
@agoodway/goodleads-server
Server-side session recording flow handlers for GoodLeads. Validates lead sessions, creates and completes session recordings via the GoodAudit API, and manages signed completion cookies. Framework-agnostic — works with any server that supports the Web Request/Response API.
Installation
npm install @agoodway/goodleads-server
# or
bun add @agoodway/goodleads-serverUsage
Start a Recording Session
import {
parseStartRouteParams,
startRecordingFlow,
json,
RecordingRouteError,
GoodAuditError,
} from "@agoodway/goodleads-server";
async function handleStartRecording(request: Request): Promise<Response> {
const params = await parseStartRouteParams(request);
if (!params) {
return json({ error: "Invalid request body" }, 400);
}
try {
const result = await startRecordingFlow(params, {
goodleads: { apiUrl: "https://api.goodleads.com" },
goodAudit: {
apiUrl: "https://api.goodaudit.com",
apiKey: "your-api-key",
},
completionCookie: {
secure: true,
secret: "your-cookie-signing-secret",
},
});
// Set the completion cookie on your response
// result.completionCookie contains { name, value, options }
return json({
session_token: result.sessionToken,
events_url: result.eventsUrl,
});
} catch (error) {
if (error instanceof RecordingRouteError) {
return json({ error: error.message }, error.status);
}
if (error instanceof GoodAuditError) {
return json({ error: error.message }, error.status);
}
return json({ error: "Internal server error" }, 500);
}
}Complete a Recording Session
import {
parseCompleteRecordingSessionId,
completeRecordingFlow,
json,
RecordingRouteError,
} from "@agoodway/goodleads-server";
async function handleCompleteRecording(request: Request): Promise<Response> {
const recordingSessionId = await parseCompleteRecordingSessionId(request);
if (!recordingSessionId) {
return json({ error: "Invalid recording_session_id" }, 400);
}
try {
const result = await completeRecordingFlow(
recordingSessionId,
getCookieValue(request, "gl_recording_complete"), // your cookie getter
{
goodAudit: {
apiUrl: "https://api.goodaudit.com",
apiKey: "your-api-key",
},
completionCookie: {
secure: true,
secret: "your-cookie-signing-secret",
},
},
);
// Clear the cookie using result.clearCookie
return json({ status: "ok" });
} catch (error) {
if (error instanceof RecordingRouteError) {
return json({ error: error.message }, error.status);
}
return json({ error: "Internal server error" }, 500);
}
}Cookie Management
The library provides helpers for building and validating signed cookies:
import {
buildCompletionCookie,
hasValidCompletionCookie,
clearCompletionCookie,
} from "@agoodway/goodleads-server";
// Build a signed cookie after starting a recording
const cookie = await buildCompletionCookie("session-id-123", {
secure: true,
secret: "your-secret",
path: "/api/recordings/complete",
ttlSeconds: 7200, // 2 hours (default)
});
// cookie = { name: "gl_recording_complete", value: "session-id-123.1234567890.signature", options: {...} }
// Validate a cookie before allowing completion
const valid = await hasValidCompletionCookie(
cookieValue,
"session-id-123",
{ secure: true, secret: "your-secret" },
);
// Clear the cookie after completion
const clear = clearCompletionCookie({ secure: true, secret: "your-secret" });
// clear.options.maxAge === 0Lead Session Validation
Validate that a recording is bound to an active lead session:
import { validateDraftLeadSession } from "@agoodway/goodleads-server";
const result = await validateDraftLeadSession(
{ accountSlug: "acme", leadId: "uuid-here", leadToken: "bearer-token" },
{ apiUrl: "https://api.goodleads.com" },
);
if (!result.valid) {
console.log(`Validation failed with status: ${result.status}`);
}API Reference
Flow Functions
startRecordingFlow(params, config)— Full start flow: validates lead session, creates recording, returns session token + events URL + cookiecompleteRecordingFlow(sessionId, cookieValue, config)— Full completion flow: validates cookie, completes recording upstream, returns clear cookie instruction
Parsing Functions
parseStartRouteParams(request)— Parse and validate a start recording request bodyparseCompleteRecordingSessionId(request)— Parse a recording session ID from a complete request body
Cookie Functions
buildCompletionCookie(sessionId, config)— Create a signed completion cookiehasValidCompletionCookie(value, sessionId, config)— Validate a signed cookie (checks expiry + signature)clearCompletionCookie(config)— Build a cookie instruction that clears the cookie
Validation Functions
validateDraftLeadSession(params, config)— Check if a lead session is active via the GoodLeads API
Utilities
json(body, status?)— Create a JSONResponse
Error Classes
GoodAuditError— Upstream GoodAudit API error (has.status)RecordingRouteError— Route-level error with HTTP status and optional metadata
Configuration Interfaces
StartRecordingFlowConfig— Config for the start flow (goodAudit + goodleads + completionCookie)CompleteRecordingFlowConfig— Config for the complete flow (goodAudit + completionCookie)GoodAuditConfig—{ apiUrl, apiKey }GoodLeadsConfig—{ apiUrl }CompletionCookieConfig—{ secure, secret, path?, name?, ttlSeconds? }
Security
- Cookies are signed with HMAC-SHA256 using the Web Crypto API
- Cookie values include an expiration timestamp to prevent replay attacks
- Lead session validation ensures recordings are tied to legitimate sessions
- All input is validated against strict patterns (UUIDs, slugs, session IDs)
License
MIT
