Co-Sell Platform Implementation Notes¶
For Patent Application Support¶
Document Classification: Patent Sensitive - Implementation Documentation Version: 1.0 Date: December 2025
1. Technology Stack¶
1.1 Core Components¶
| Component | Technology | Purpose |
|---|---|---|
| Backend API | Node.js / TypeScript | REST API for all platform operations |
| Database | PostgreSQL | Primary data store for partners, relationships, campaigns |
| Cache | Redis | Session management, rate limiting, temporary scoring cache |
| Message Queue | Redis / Bull | Async job processing for content generation, publishing |
| AI/LLM | Claude 3 Sonnet API | Content generation, analysis, predictions |
| Embeddings | OpenAI Ada-002 or Claude | Similarity detection for duplicate content |
| File Storage | AWS S3 | Generated assets, brand assets, campaign media |
| Search | Elasticsearch | Partner discovery, content search |
1.2 External Integrations¶
| Integration | Purpose | Authentication |
|---|---|---|
| LinkedIn API | Social content publishing | OAuth 2.0 |
| SendGrid/Mailchimp | Email campaign delivery | API Key |
| Salesforce | CRM sync, lead tracking | OAuth 2.0 |
| HubSpot | Marketing automation sync | OAuth 2.0 |
| AWS Marketplace API | Partner data enrichment | AWS IAM |
| Segment | Analytics event tracking | API Key |
2. Database Schema¶
2.1 Core Tables¶
-- Partners table
CREATE TABLE partners (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
company_name VARCHAR(255) NOT NULL,
industry VARCHAR(100),
company_size VARCHAR(50),
aws_partner_tier VARCHAR(50),
product_description TEXT,
icp_definition JSONB,
brand_voice JSONB,
value_propositions JSONB,
target_markets JSONB,
product_categories JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
status VARCHAR(50) DEFAULT 'active'
);
-- Indexes for common queries
CREATE INDEX idx_partners_industry ON partners(industry);
CREATE INDEX idx_partners_tier ON partners(aws_partner_tier);
CREATE INDEX idx_partners_status ON partners(status);
CREATE INDEX idx_partners_icp ON partners USING GIN(icp_definition);
-- Co-sell relationships table
CREATE TABLE cosell_relationships (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
partner_a_id UUID REFERENCES partners(id),
partner_b_id UUID REFERENCES partners(id),
status VARCHAR(50) DEFAULT 'invited',
initiated_by UUID REFERENCES partners(id),
match_scores JSONB,
success_prediction JSONB,
created_at TIMESTAMP DEFAULT NOW(),
accepted_at TIMESTAMP,
ended_at TIMESTAMP,
end_reason VARCHAR(255),
CONSTRAINT unique_relationship UNIQUE (partner_a_id, partner_b_id)
);
CREATE INDEX idx_relationships_status ON cosell_relationships(status);
CREATE INDEX idx_relationships_partner_a ON cosell_relationships(partner_a_id);
CREATE INDEX idx_relationships_partner_b ON cosell_relationships(partner_b_id);
-- Campaign plans table
CREATE TABLE cosell_plans (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
relationship_id UUID REFERENCES cosell_relationships(id),
name VARCHAR(255),
type VARCHAR(50),
status VARCHAR(50) DEFAULT 'draft',
merged_brand_voice JSONB,
objectives JSONB,
content_calendar JSONB,
metrics JSONB,
start_date DATE,
end_date DATE,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_plans_relationship ON cosell_plans(relationship_id);
CREATE INDEX idx_plans_status ON cosell_plans(status);
-- Shared assets table
CREATE TABLE shared_assets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
plan_id UUID REFERENCES cosell_plans(id),
asset_type VARCHAR(50),
title VARCHAR(500),
content JSONB,
partner_a_approval VARCHAR(50) DEFAULT 'pending',
partner_b_approval VARCHAR(50) DEFAULT 'pending',
scheduled_publish_date TIMESTAMP,
published_at TIMESTAMP,
distribution_channels JSONB,
tracking_parameters JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_assets_plan ON shared_assets(plan_id);
CREATE INDEX idx_assets_status ON shared_assets(partner_a_approval, partner_b_approval);
CREATE INDEX idx_assets_scheduled ON shared_assets(scheduled_publish_date);
2.2 Audit and History Tables¶
-- Audit log for all changes
CREATE TABLE audit_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entity_type VARCHAR(50),
entity_id UUID,
action VARCHAR(50),
actor_id UUID,
actor_type VARCHAR(50),
old_values JSONB,
new_values JSONB,
metadata JSONB,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_audit_entity ON audit_log(entity_type, entity_id);
CREATE INDEX idx_audit_actor ON audit_log(actor_id);
CREATE INDEX idx_audit_created ON audit_log(created_at);
-- Asset approval history
CREATE TABLE asset_approval_history (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
asset_id UUID REFERENCES shared_assets(id),
partner_id UUID REFERENCES partners(id),
action VARCHAR(50),
comment TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_approval_history_asset ON asset_approval_history(asset_id);
3. API Endpoints¶
3.1 Partner Matching API¶
POST /api/v1/partners/{partner_id}/match-requests
Request matching candidates for a partner
Body: { filters: { industry?, tier?, min_score? } }
Response: { matches: [{ partner, scores, prediction }] }
GET /api/v1/partners/{partner_id}/matches
Get all qualified matches for a partner
Query: ?status=qualified&min_score=50&limit=20
Response: { matches: [...], total: number }
GET /api/v1/matches/{match_id}/scores
Get detailed scoring breakdown
Response: { icp_overlap: {...}, product_fit: {...}, market: {...} }
3.2 Relationship API¶
POST /api/v1/relationships
Create new co-sell relationship (send invitation)
Body: { partner_a_id, partner_b_id }
Response: { relationship_id, status: 'invited' }
PATCH /api/v1/relationships/{id}
Update relationship status (accept/decline/pause/end)
Body: { status: 'active' | 'declined' | 'paused' | 'ended', reason? }
Response: { relationship }
GET /api/v1/relationships/{id}
Get relationship details
Response: { relationship, match_scores, prediction, plans: [...] }
3.3 Campaign API¶
POST /api/v1/relationships/{relationship_id}/plans
Create new campaign plan
Body: { type, duration_weeks, objectives? }
Response: { plan_id, calendar, assets: [...] }
GET /api/v1/plans/{plan_id}
Get plan with all assets
Response: { plan, calendar, assets, merged_voice }
POST /api/v1/plans/{plan_id}/regenerate
Regenerate campaign with new parameters
Body: { focus?, tone_adjustment?, exclude_assets? }
Response: { plan, assets }
3.4 Asset Approval API¶
PATCH /api/v1/assets/{asset_id}/approval
Submit approval decision
Body: { partner_id, decision: 'approved' | 'changes_requested' | 'rejected', comment? }
Response: { asset, ready_for_publishing: boolean }
POST /api/v1/assets/{asset_id}/revisions
Submit revision to asset
Body: { content: {...}, revision_notes }
Response: { asset, version: number }
GET /api/v1/assets/{asset_id}/history
Get approval and revision history
Response: { history: [...] }
3.5 Publishing API¶
POST /api/v1/assets/{asset_id}/schedule
Schedule asset for publishing
Body: { publish_time, channels: [...] }
Response: { scheduled_jobs: [...] }
POST /api/v1/assets/{asset_id}/publish
Immediately publish asset
Body: { channels: [...] }
Response: { results: [{ channel, status, url? }] }
GET /api/v1/plans/{plan_id}/publishing-status
Get publishing status for all plan assets
Response: { assets: [{ id, status, published_urls }] }
4. Algorithm Implementation Details¶
4.1 ICP Overlap Scoring¶
interface ICPDefinition {
company_size_range: [number, number];
industries: string[];
geographic_regions: string[];
technologies: string[];
}
interface ICPOverlapResult {
score: number;
breakdown: {
company_size: number;
industry: number;
geography: number;
technology: number;
};
}
function calculateICPOverlap(icpA: ICPDefinition, icpB: ICPDefinition): ICPOverlapResult {
const weights = {
company_size: 0.25,
industry: 0.30,
geography: 0.25,
technology: 0.20
};
// Company size overlap (range intersection)
const sizeOverlap = calculateRangeOverlap(
icpA.company_size_range,
icpB.company_size_range
);
const sizeScore = sizeOverlap * weights.company_size * 100;
// Industry overlap (Jaccard index)
const industryJaccard = calculateJaccardIndex(icpA.industries, icpB.industries);
const industryScore = industryJaccard * weights.industry * 100;
// Geography overlap (Jaccard index)
const geoJaccard = calculateJaccardIndex(icpA.geographic_regions, icpB.geographic_regions);
const geoScore = geoJaccard * weights.geography * 100;
// Technology overlap (Jaccard index)
const techJaccard = calculateJaccardIndex(icpA.technologies, icpB.technologies);
const techScore = techJaccard * weights.technology * 100;
const totalScore = Math.round(sizeScore + industryScore + geoScore + techScore);
return {
score: totalScore,
breakdown: {
company_size: Math.round(sizeScore),
industry: Math.round(industryScore),
geography: Math.round(geoScore),
technology: Math.round(techScore)
}
};
}
function calculateJaccardIndex(setA: string[], setB: string[]): number {
const intersection = setA.filter(x => setB.includes(x));
const union = [...new Set([...setA, ...setB])];
if (union.length === 0) return 0;
return intersection.length / union.length;
}
function calculateRangeOverlap(rangeA: [number, number], rangeB: [number, number]): number {
const overlapStart = Math.max(rangeA[0], rangeB[0]);
const overlapEnd = Math.min(rangeA[1], rangeB[1]);
if (overlapStart > overlapEnd) return 0;
const overlapLength = overlapEnd - overlapStart;
const unionLength = Math.max(rangeA[1], rangeB[1]) - Math.min(rangeA[0], rangeB[0]);
return overlapLength / unionLength;
}
4.2 Product Complementarity Analysis¶
interface ComplementarityResult {
score: number;
competitive_overlap: number;
ai_analysis: {
complementarity_score: number;
integration_potential: number;
customer_use_case_fit: number;
reasoning: string;
};
}
async function calculateProductComplementarity(
partnerA: Partner,
partnerB: Partner
): Promise<ComplementarityResult> {
// Step 1: Check category overlap (competition detection)
const competitiveOverlap = calculateCategoryOverlap(
partnerA.product_categories,
partnerB.product_categories
);
if (competitiveOverlap > 0.7) {
return {
score: 0,
competitive_overlap: competitiveOverlap,
ai_analysis: null // Disqualified
};
}
// Step 2: AI analysis
const prompt = constructComplementarityPrompt(partnerA, partnerB);
const aiAnalysis = await claude.invoke({
model: 'claude-3-sonnet',
prompt: prompt,
response_format: {
type: 'json_schema',
schema: {
complementarity_score: 'integer',
integration_potential: 'integer',
customer_use_case_fit: 'integer',
reasoning: 'string'
}
}
});
// Step 3: Calculate weighted score
const rawScore =
aiAnalysis.complementarity_score * 0.40 +
aiAnalysis.integration_potential * 0.30 +
aiAnalysis.customer_use_case_fit * 0.30;
// Step 4: Apply competition penalty
const finalScore = Math.round(rawScore * (1 - competitiveOverlap));
return {
score: finalScore,
competitive_overlap: competitiveOverlap,
ai_analysis: aiAnalysis
};
}
function constructComplementarityPrompt(partnerA: Partner, partnerB: Partner): string {
return `
Analyze the business complementarity between these two software companies:
Company A: ${partnerA.company_name}
Product: ${partnerA.product_description}
Value Props: ${partnerA.value_propositions.join(', ')}
Company B: ${partnerB.company_name}
Product: ${partnerB.product_description}
Value Props: ${partnerB.value_propositions.join(', ')}
Rate the following on a scale of 0-100:
1. complementarity_score: How well do these products complement each other?
2. integration_potential: How feasible is technical or workflow integration?
3. customer_use_case_fit: Would shared customers benefit from using both?
Provide brief reasoning for your scores.
`;
}
4.3 Brand Voice Merging¶
interface MergedBrandVoice {
harmonized_tone: string[];
shared_pain_points: string[];
combined_goals: string[];
messaging_guidelines: {
do: string[];
avoid: string[];
conflicts_resolved: ConflictResolution[];
};
joint_value_propositions: string[];
co_branding_guidelines: CoBrandingGuidelines;
}
const TONE_SPECTRUM: Record<string, number> = {
'formal': 0.9,
'professional': 0.7,
'business-casual': 0.5,
'approachable': 0.4,
'casual': 0.2,
'playful': 0.1
};
function harmonizeTones(toneA: string[], toneB: string[]): string[] {
// Calculate average position on spectrum
const avgA = toneA.reduce((sum, t) => sum + (TONE_SPECTRUM[t] || 0.5), 0) / toneA.length;
const avgB = toneB.reduce((sum, t) => sum + (TONE_SPECTRUM[t] || 0.5), 0) / toneB.length;
const mergedPosition = (avgA + avgB) / 2;
// Find tones within 0.2 of merged position
const harmonized: string[] = [];
for (const [tone, value] of Object.entries(TONE_SPECTRUM)) {
if (Math.abs(value - mergedPosition) < 0.2) {
harmonized.push(tone);
}
}
// Add shared tones
const shared = toneA.filter(t => toneB.includes(t));
const result = [...new Set([...harmonized, ...shared])];
return result.slice(0, 3); // Return top 3
}
async function mergeBrandVoices(
brandA: BrandVoice,
brandB: BrandVoice,
campaignContext: CampaignContext
): Promise<MergedBrandVoice> {
// Step 1: Tone harmonization
const harmonizedTone = harmonizeTones(brandA.tone, brandB.tone);
// Step 2: Audience intersection
const sharedPainPoints = brandA.target_audience.pain_points.filter(
p => brandB.target_audience.pain_points.some(bp => semanticallySimilar(p, bp))
);
const combinedGoals = [...new Set([
...brandA.target_audience.goals,
...brandB.target_audience.goals
])];
// Step 3: Messaging merge with conflict detection
const conflicts = identifyMessagingConflicts(brandA, brandB);
const conflictsResolved = await resolveConflictsWithAI(conflicts);
const messagingDo = [...new Set([...brandA.messaging_guidelines.do, ...brandB.messaging_guidelines.do])];
const messagingAvoid = [...new Set([...brandA.messaging_guidelines.avoid, ...brandB.messaging_guidelines.avoid])];
// Step 4: AI-generated joint value propositions
const jointValueProps = await generateJointValuePropositions(brandA, brandB, campaignContext);
// Step 5: Co-branding guidelines
const coBrandingGuidelines = {
company_mention_order: [brandA.company_name, brandB.company_name].sort().join(' and '),
logo_placement: 'equal',
voice_balance: { [brandA.company_name]: 0.5, [brandB.company_name]: 0.5 },
attribution_format: `${brandA.company_name} and ${brandB.company_name}`
};
return {
harmonized_tone: harmonizedTone,
shared_pain_points: sharedPainPoints,
combined_goals: combinedGoals,
messaging_guidelines: {
do: messagingDo,
avoid: messagingAvoid,
conflicts_resolved: conflictsResolved
},
joint_value_propositions: jointValueProps,
co_branding_guidelines: coBrandingGuidelines
};
}
5. Job Queue Architecture¶
5.1 Queue Definitions¶
// Queue names and their purposes
const QUEUES = {
CONTENT_GENERATION: 'content-generation', // AI content generation jobs
PUBLISHING: 'publishing', // Social/email publishing
NOTIFICATIONS: 'notifications', // Partner notifications
ANALYTICS: 'analytics', // Event processing
MATCHING: 'matching' // Partner matching calculations
};
// Job processors
const jobProcessors = {
[QUEUES.CONTENT_GENERATION]: async (job) => {
const { assetId, assetType, mergedVoice, objectives } = job.data;
const content = await generateCobrandedContent(assetType, mergedVoice, objectives);
await updateAsset(assetId, { content, status: 'pending_approval' });
await notifyPartners(assetId, 'content_ready_for_review');
},
[QUEUES.PUBLISHING]: async (job) => {
const { assetId, channel, scheduledTime } = job.data;
// Wait until scheduled time
if (Date.now() < scheduledTime) {
throw new Error('Not yet scheduled'); // Will be retried
}
const asset = await getAsset(assetId);
const result = await publishToChannel(channel, asset.content);
await updateAsset(assetId, {
published_at: new Date(),
[`${channel}_result`]: result
});
}
};
5.2 Retry Configuration¶
const queueOptions = {
[QUEUES.CONTENT_GENERATION]: {
attempts: 3,
backoff: {
type: 'exponential',
delay: 5000 // 5s, 10s, 20s
}
},
[QUEUES.PUBLISHING]: {
attempts: 5,
backoff: {
type: 'exponential',
delay: 60000 // 1m, 2m, 4m, 8m, 16m
}
}
};
6. Caching Strategy¶
6.1 Cache Keys and TTLs¶
const CACHE_CONFIG = {
// Partner matching scores - cache for 1 hour
MATCH_SCORES: {
pattern: 'match:scores:{partnerA}:{partnerB}',
ttl: 3600
},
// Brand voice merge results - cache for 24 hours
MERGED_VOICE: {
pattern: 'merged:voice:{relationshipId}:{campaignType}',
ttl: 86400
},
// Success predictions - cache for 6 hours
PREDICTIONS: {
pattern: 'prediction:{relationshipId}',
ttl: 21600
},
// Partner profile summary - cache for 15 minutes
PARTNER_SUMMARY: {
pattern: 'partner:summary:{partnerId}',
ttl: 900
}
};
6.2 Cache Invalidation¶
// Events that trigger cache invalidation
const INVALIDATION_TRIGGERS = {
'partner.profile.updated': ['PARTNER_SUMMARY', 'MATCH_SCORES'],
'partner.icp.updated': ['MATCH_SCORES', 'PREDICTIONS'],
'partner.brand_voice.updated': ['MERGED_VOICE'],
'relationship.created': ['PREDICTIONS'],
'relationship.status.changed': ['PREDICTIONS']
};
7. Monitoring and Metrics¶
7.1 Key Metrics to Track¶
const METRICS = {
// Matching metrics
'matching.requests': 'counter',
'matching.duration_ms': 'histogram',
'matching.qualified_count': 'histogram',
'matching.disqualified_count': 'histogram',
// Content generation metrics
'content.generation.requests': 'counter',
'content.generation.duration_ms': 'histogram',
'content.generation.failures': 'counter',
'content.generation.token_usage': 'counter',
// Approval metrics
'approval.time_to_decision_hours': 'histogram',
'approval.revisions_per_asset': 'histogram',
'approval.rejection_rate': 'gauge',
// Publishing metrics
'publishing.success_rate': 'gauge',
'publishing.failures_by_channel': 'counter',
'publishing.retry_count': 'histogram',
// Business metrics
'relationships.active': 'gauge',
'campaigns.active': 'gauge',
'assets.published_total': 'counter'
};
7.2 Alerting Thresholds¶
const ALERTS = {
'content.generation.failures': {
threshold: 5,
window: '5m',
severity: 'warning'
},
'publishing.success_rate': {
threshold: 0.95,
operator: 'lt',
severity: 'critical'
},
'approval.time_to_decision_hours': {
threshold: 168, // 7 days
percentile: 'p95',
severity: 'warning'
}
};
8. Security Implementation¶
8.1 Authentication¶
// JWT token structure
interface JWTPayload {
sub: string; // User ID
partner_id: string; // Partner organization
roles: string[]; // ['admin', 'editor', 'viewer']
permissions: string[]; // ['campaigns.create', 'assets.approve']
exp: number; // Expiration timestamp
}
// Middleware for route protection
function requirePermission(permission: string) {
return (req, res, next) => {
if (!req.user.permissions.includes(permission)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
8.2 Data Access Control¶
// Row-level security for partner data
function enforcePartnerAccess(query: Query, partnerId: string): Query {
return query.where(builder => {
builder
.where('partner_a_id', partnerId)
.orWhere('partner_b_id', partnerId);
});
}
// Sensitive field redaction
function redactSensitiveFields(partner: Partner, viewerPartnerId: string): Partner {
if (partner.id === viewerPartnerId) {
return partner; // Full access to own data
}
// Redact sensitive fields for other partners
return {
...partner,
icp_definition: {
...partner.icp_definition,
budget_range: undefined // Don't expose budget info
}
};
}
Document Version: 1.0 For Patent Application Support Confidential - Implementation Documentation