Skip to main content

Social Models

This document covers the social integration data models for the CRM & Marketing Platform.


SocialConnection

OAuth connection to a social platform.

model SocialConnection {
id String @id @default(uuid())
tenantId String
clubId String?

platform SocialPlatform
accountId String // Platform's account/page ID
accountName String // Display name
accountType String? // page, profile, business

// OAuth tokens (encrypted at rest)
accessToken String
refreshToken String?
tokenExpiry DateTime?

// Permissions granted
permissions String[] @default([])

// Status
isActive Boolean @default(true)
lastUsedAt DateTime?
lastErrorAt DateTime?
lastError String?

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
connectedBy String

// Relations
posts SocialPost[]

@@unique([tenantId, platform, accountId])
@@index([tenantId, platform, isActive])
@@index([tenantId, clubId])
}

Platforms

PlatformAccount TypesPermissions
FACEBOOKpagepages_manage_posts, pages_read_engagement
INSTAGRAMbusinessinstagram_basic, instagram_content_publish
TWITTERprofiletweet.read, tweet.write
LINKEDINcompanyw_member_social (future)

Token Security

  • accessToken - Encrypted with AES-256
  • refreshToken - Stored separately, encrypted
  • Never exposed in API responses
  • Decrypted only at publish time

SocialPost

Social media post.

model SocialPost {
id String @id @default(uuid())
tenantId String

connectionId String
connection SocialConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)

// Optional campaign link
campaignId String?
campaign Campaign? @relation(fields: [campaignId], references: [id])

// Content
content String
mediaUrls String[] @default([])
linkUrl String?
hashtags String[] @default([])

// Schedule & Status
status SocialPostStatus @default(DRAFT)
scheduledAt DateTime?
publishedAt DateTime?

// Platform response
platformPostId String? // ID returned by platform
platformUrl String? // Direct link to post
error String?

// Engagement metrics (synced from platform)
likes Int @default(0)
comments Int @default(0)
shares Int @default(0)
reach Int @default(0)
impressions Int @default(0)
engagementSyncedAt DateTime?

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdBy String

@@index([tenantId, status])
@@index([tenantId, scheduledAt])
@@index([connectionId])
@@index([campaignId])
}

Post Status Flow

DRAFT → SCHEDULED → PUBLISHING → PUBLISHED
↓ ↓ ↓
Edit Cancel (auto) → Sync metrics

FAILED ←── On error

Content Guidelines

PlatformMax LengthMedia
Facebook63,206 charsImages, Video
Instagram2,200 charsRequired image/video
Twitter280 charsOptional images

EventPromotion

Configuration for auto-promoting events/tournaments.

model EventPromotion {
id String @id @default(uuid())
tenantId String
clubId String

// Source event
sourceType EventSourceType
sourceId String // TeeTime.Competition.id or similar

// Settings
autoPromote Boolean @default(true)
channels Channel[]

// Timing
announceAt DateTime? // When to first announce
reminderDays Int[] @default([7, 3, 1]) // Days before event

// Targeting
segmentId String? // Custom segment (null = all members)
targetRadius Int? // km from club for geo-targeting

// Content
emailTemplateId String?
smsTemplateId String?
socialContent String? // Custom social post text

// Status
isActive Boolean @default(true)
lastPromotedAt DateTime?

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdBy String

@@unique([tenantId, sourceType, sourceId])
@@index([tenantId, clubId])
@@index([tenantId, isActive])
}

Event Source Types

TypeSourceTrigger Event
COMPETITIONTeeTimecompetition.published
CLUB_EVENTClub Eventsclub_event.published
SPECIALManualManual creation

Reminder Schedule

{
"reminderDays": [7, 3, 1]
}

Creates reminders:

  • 7 days before event
  • 3 days before event
  • 1 day before event

Data Dictionary

SocialConnection Fields

FieldTypeDescription
platformEnumFACEBOOK, INSTAGRAM, TWITTER, etc.
accountIdStringPlatform's page/account ID
accessTokenStringEncrypted OAuth token
refreshTokenStringEncrypted refresh token
tokenExpiryDateTimeWhen token expires
permissionsArrayGranted OAuth permissions
isActiveBooleanConnection usable

SocialPost Fields

FieldTypeDescription
contentStringPost text
mediaUrlsArrayImage/video URLs
statusEnumDRAFT → PUBLISHED
platformPostIdStringID from platform after publish
likesIntSynced engagement

EventPromotion Fields

FieldTypeDescription
sourceTypeEnumCOMPETITION, CLUB_EVENT, SPECIAL
sourceIdStringTeeTime.Competition.id
autoPromoteBooleanEnable auto-promotion
channelsArrayChannels to promote on
reminderDaysInt[]Days before event to send reminders

Index Strategy

Query PatternIndex
List connections by platform(tenantId, platform, isActive)
Filter connections by club(tenantId, clubId)
List posts by status(tenantId, status)
Find scheduled posts(tenantId, scheduledAt)
Posts for connection(connectionId)
Posts for campaign(campaignId)
Active promotions(tenantId, isActive)
Promotions by club(tenantId, clubId)