(null)
{{ formatPersonName(app.candidateFirstName, app.candidateLastName) }}
@@ -777,5 +782,6 @@ const selectedApplicationId = ref(null)
v-if="selectedApplicationId"
:application-id="selectedApplicationId"
@close="selectedApplicationId = null"
+ @deleted="handleApplicationDeleted"
/>
diff --git a/app/pages/dashboard/candidates/index.vue b/app/pages/dashboard/candidates/index.vue
index 55f3ab8a..d8b608e8 100644
--- a/app/pages/dashboard/candidates/index.vue
+++ b/app/pages/dashboard/candidates/index.vue
@@ -587,7 +587,7 @@ const selectedCandidateId = ref(null)
{{ formatCandidateName(c) }}
diff --git a/app/pages/dashboard/settings/notifications.vue b/app/pages/dashboard/settings/notifications.vue
new file mode 100644
index 00000000..f4cda562
--- /dev/null
+++ b/app/pages/dashboard/settings/notifications.vue
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+ Notifications
+
+
+ Choose which events email you, and whether instantly or batched into a daily digest.
+
+
+
+
+
+
+
+
+
+
+
Email notifications
+
These preferences apply to you in this organization.
+
+
+
+
+
+
+
+
+ Could not load notification preferences.
+ Retry
+
+
+
+
+
+ {{ NOTIFICATION_TYPE_META[type].label }}
+
+
+ {{ NOTIFICATION_TYPE_META[type].description }}
+
+
+
+
+
+ {{ mode.label }}
+ {{ mode.hint }}
+
+
+
+
+
+
+
+ {{ saveError }}
+
+
+
+
+
+
+
+ {{ saveSuccess ? 'Saved!' : isSaving ? 'Saving…' : 'Save changes' }}
+
+
+
+
+
+
diff --git a/e2e/critical-flows/application-deletion.spec.ts b/e2e/critical-flows/application-deletion.spec.ts
new file mode 100644
index 00000000..590eeab1
--- /dev/null
+++ b/e2e/critical-flows/application-deletion.spec.ts
@@ -0,0 +1,85 @@
+import { test, expect } from '../fixtures'
+
+async function createJob(page: Parameters[1]>[0]['page']) {
+ const response = await page.request.post('/api/jobs', {
+ data: {
+ title: `Application deletion role ${Date.now()}`,
+ description: 'Exercises application and candidate deletion behavior.',
+ location: 'Oslo, Norway',
+ type: 'full_time',
+ status: 'open',
+ phoneRequirement: 'optional',
+ requireResume: false,
+ requireCoverLetter: false,
+ autoScoreOnApply: false,
+ questions: [],
+ criteria: [],
+ },
+ })
+ expect(response.status()).toBe(201)
+ return response.json() as Promise<{ id: string }>
+}
+
+async function createCandidate(
+ page: Parameters[1]>[0]['page'],
+ label: string,
+) {
+ const response = await page.request.post('/api/candidates', {
+ data: {
+ firstName: label,
+ lastName: 'Deletion Test',
+ email: `${label.toLowerCase()}-${Date.now()}@example.com`,
+ },
+ })
+ expect(response.status()).toBe(201)
+ return response.json() as Promise<{ id: string, firstName: string, lastName: string }>
+}
+
+async function createApplication(
+ page: Parameters[1]>[0]['page'],
+ candidateId: string,
+ jobId: string,
+) {
+ const response = await page.request.post('/api/applications', {
+ data: { candidateId, jobId },
+ })
+ expect(response.status()).toBe(201)
+ return response.json() as Promise<{ id: string }>
+}
+
+test('applications can be deleted independently and quarantined candidates disappear from pipelines', async ({ authenticatedPage }) => {
+ const page = authenticatedPage
+ const job = await createJob(page)
+ const retainedCandidate = await createCandidate(page, 'Retained')
+ const deletedApplication = await createApplication(page, retainedCandidate.id, job.id)
+
+ await page.goto('/dashboard/applications')
+ await page.waitForLoadState('networkidle')
+ await page.getByRole('button', { name: 'Retained Deletion Test' }).click()
+ await expect(page.getByRole('dialog', { name: 'Application detail' })).toBeVisible()
+ await page.getByRole('button', { name: 'Delete application' }).click()
+
+ const confirmation = page.getByRole('dialog', { name: 'Delete Application' })
+ await expect(confirmation).toContainText('The candidate remains in your candidate list.')
+
+ const deleteResponse = page.waitForResponse(response =>
+ response.request().method() === 'DELETE'
+ && new URL(response.url()).pathname === `/api/applications/${deletedApplication.id}`,
+ )
+ await confirmation.getByRole('button', { name: 'Delete application' }).click()
+ expect((await deleteResponse).status()).toBe(204)
+
+ await expect(page.getByRole('button', { name: 'Retained Deletion Test' })).toHaveCount(0)
+ expect((await page.request.get(`/api/candidates/${retainedCandidate.id}`)).status()).toBe(200)
+ expect((await page.request.get(`/api/applications/${deletedApplication.id}`)).status()).toBe(404)
+
+ const quarantinedCandidate = await createCandidate(page, 'Quarantined')
+ const hiddenApplication = await createApplication(page, quarantinedCandidate.id, job.id)
+ expect((await page.request.delete(`/api/candidates/${quarantinedCandidate.id}`)).status()).toBe(200)
+
+ const applicationsResponse = await page.request.get('/api/applications?limit=100')
+ expect(applicationsResponse.status()).toBe(200)
+ const applications = await applicationsResponse.json() as { data: Array<{ id: string }> }
+ expect(applications.data.map(row => row.id)).not.toContain(hiddenApplication.id)
+ expect((await page.request.get(`/api/applications/${hiddenApplication.id}`)).status()).toBe(404)
+})
diff --git a/ee/server/api/source-tracking/stats.get.ts b/ee/server/api/source-tracking/stats.get.ts
index c74ebbb6..477c4f6e 100644
--- a/ee/server/api/source-tracking/stats.get.ts
+++ b/ee/server/api/source-tracking/stats.get.ts
@@ -1,4 +1,4 @@
-import { eq, and, sql, count, gte, lte, desc } from 'drizzle-orm'
+import { eq, and, sql, count, gte, isNull, lte, desc } from 'drizzle-orm'
import { applicationSource, application, trackingLink, job, candidate } from '~~/server/database/schema'
import { sourceStatsQuerySchema } from '~~/server/utils/schemas/trackingLink'
@@ -22,7 +22,10 @@ export default defineEventHandler(async (event) => {
const query = await getValidatedQuery(event, sourceStatsQuerySchema.parse)
// Build date range conditions
- const dateConditions = [eq(applicationSource.organizationId, orgId)]
+ const dateConditions = [
+ eq(applicationSource.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ]
if (query.jobId) {
dateConditions.push(eq(application.jobId, query.jobId))
}
@@ -56,6 +59,7 @@ export default defineEventHandler(async (event) => {
})
.from(applicationSource)
.innerJoin(application, eq(application.id, applicationSource.applicationId))
+ .innerJoin(candidate, eq(candidate.id, application.candidateId))
.where(whereClause)
.groupBy(applicationSource.channel)
.orderBy(sql`count(*) desc`),
@@ -69,13 +73,20 @@ export default defineEventHandler(async (event) => {
code: trackingLink.code,
jobTitle: job.title,
clickCount: trackingLink.clickCount,
- applicationCount: trackingLink.applicationCount,
+ applicationCount: count(candidate.id).as('application_count'),
isActive: trackingLink.isActive,
})
.from(trackingLink)
.leftJoin(job, eq(job.id, trackingLink.jobId))
+ .leftJoin(applicationSource, eq(applicationSource.trackingLinkId, trackingLink.id))
+ .leftJoin(application, eq(application.id, applicationSource.applicationId))
+ .leftJoin(candidate, and(
+ eq(candidate.id, application.candidateId),
+ isNull(candidate.quarantinedAt),
+ ))
.where(eq(trackingLink.organizationId, orgId))
- .orderBy(desc(trackingLink.applicationCount))
+ .groupBy(trackingLink.id, job.title)
+ .orderBy(desc(count(candidate.id)))
.limit(10),
// 3. Conversion funnel — application status breakdown per channel
@@ -87,6 +98,7 @@ export default defineEventHandler(async (event) => {
})
.from(applicationSource)
.innerJoin(application, eq(application.id, applicationSource.applicationId))
+ .innerJoin(candidate, eq(candidate.id, application.candidateId))
.where(whereClause)
.groupBy(applicationSource.channel, application.status),
@@ -99,6 +111,7 @@ export default defineEventHandler(async (event) => {
})
.from(applicationSource)
.innerJoin(application, eq(application.id, applicationSource.applicationId))
+ .innerJoin(candidate, eq(candidate.id, application.candidateId))
.where(and(
...dateConditions,
gte(applicationSource.createdAt, sql`now() - interval '30 days'`),
@@ -133,12 +146,24 @@ export default defineEventHandler(async (event) => {
.limit(200),
// 6. Total tracked applications (have a source)
- db.$count(applicationSource, eq(applicationSource.organizationId, orgId)),
+ db
+ .select({ count: count() })
+ .from(applicationSource)
+ .innerJoin(application, eq(application.id, applicationSource.applicationId))
+ .innerJoin(candidate, eq(candidate.id, application.candidateId))
+ .where(and(
+ eq(applicationSource.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ))
+ .then(rows => Number(rows[0]?.count ?? 0)),
// 7. Total untracked applications (no source record)
db.execute(sql`
SELECT count(*) as count
FROM ${application} a
+ INNER JOIN ${candidate} c
+ ON c.id = a.candidate_id
+ AND c.quarantined_at IS NULL
WHERE a.organization_id = ${orgId}
AND NOT EXISTS (
SELECT 1 FROM ${applicationSource} s
@@ -154,6 +179,7 @@ export default defineEventHandler(async (event) => {
})
.from(applicationSource)
.innerJoin(application, eq(application.id, applicationSource.applicationId))
+ .innerJoin(candidate, eq(candidate.id, application.candidateId))
.where(and(
...dateConditions,
sql`${applicationSource.referrerDomain} IS NOT NULL`,
diff --git a/ee/server/api/webhooks/resend.post.ts b/ee/server/api/webhooks/resend.post.ts
index ef4699a4..9d676809 100644
--- a/ee/server/api/webhooks/resend.post.ts
+++ b/ee/server/api/webhooks/resend.post.ts
@@ -6,7 +6,9 @@ import {
candidateMessageAttachment,
candidateMessageWebhookEvent,
} from '~~/server/database/schema'
-import { getResendReceivingClient } from '~~/server/utils/email'
+import { enqueueNotification } from '~~/server/utils/notifications/enqueue'
+import { getResendClient, getResendReceivingClient } from '~~/server/utils/email'
+import { processNotificationStatusEvent } from '~~/server/utils/notifications/delivery-status'
import { deleteFromS3, uploadToS3 } from '~~/server/utils/s3'
import {
CANDIDATE_MESSAGE_MAX_ATTACHMENT_BYTES,
@@ -38,7 +40,10 @@ const STATUS_BY_EVENT = {
} as const
export default defineEventHandler(async (event) => {
- const { replyDomain, webhookSecret } = requireCandidateMessagingConfig()
+ const webhookSecret = env.RESEND_WEBHOOK_SECRET
+ if (!webhookSecret) {
+ throw createError({ statusCode: 503, statusMessage: 'Resend webhook is not configured' })
+ }
const payload = await readRawBody(event, 'utf8')
const webhookId = getHeader(event, 'svix-id')
const timestamp = getHeader(event, 'svix-timestamp')
@@ -47,8 +52,8 @@ export default defineEventHandler(async (event) => {
throw createError({ statusCode: 400, statusMessage: 'Missing webhook payload or signature headers' })
}
- const resend = getResendReceivingClient()
- if (!resend) throw createError({ statusCode: 503, statusMessage: 'Resend Receiving is not configured' })
+ const resend = getResendReceivingClient() ?? getResendClient()
+ if (!resend) throw createError({ statusCode: 503, statusMessage: 'Resend is not configured' })
let verified: WebhookEventPayload
try {
@@ -79,6 +84,7 @@ export default defineEventHandler(async (event) => {
try {
if (verified.type === 'email.received') {
+ const { replyDomain } = requireCandidateMessagingConfig()
await processInboundMessage(verified, replyDomain)
} else if (verified.type in STATUS_BY_EVENT) {
await processStatusEvent(verified as StatusWebhookEvent)
@@ -104,6 +110,13 @@ export default defineEventHandler(async (event) => {
type StatusWebhookEvent = Extract
async function processStatusEvent(event: StatusWebhookEvent): Promise {
+ // Recruiter notifications share this webhook — route their delivery events to
+ // the outbox instead of the candidate-message table.
+ if (event.data.tags?.category === 'notification') {
+ await processNotificationStatusEvent(event)
+ return
+ }
+
const status = STATUS_BY_EVENT[event.type]
const eventAt = new Date(event.created_at)
const taggedMessageId = event.data.tags?.message
@@ -154,7 +167,10 @@ async function processInboundMessage(
where: eq(candidateConversation.replyToken, token),
with: {
application: {
- with: { candidate: { columns: { email: true } } },
+ with: {
+ candidate: { columns: { email: true, firstName: true, lastName: true } },
+ job: { columns: { title: true } },
+ },
},
},
})
@@ -188,38 +204,58 @@ async function processInboundMessage(
return entry?.[1]
}
const createdAt = new Date(data.created_at)
- const [inserted] = await db.insert(candidateMessage).values({
- organizationId: conversation.organizationId,
- conversationId: conversation.id,
- direction: 'inbound',
- status: 'delivered',
- fromEmail,
- toEmail: normalizeEmailAddress(data.to[0] ?? event.data.to[0] ?? ''),
- subject: data.subject || event.data.subject || '(No subject)',
- bodyText: inboundTextContent(data.text, data.html),
- providerMessageId: data.id,
- internetMessageId: data.message_id,
- inReplyTo: header('in-reply-to'),
- references: parseReferences(header('references')),
- providerStatusAt: createdAt,
- sentAt: createdAt,
- deliveredAt: createdAt,
- createdAt,
- updatedAt: createdAt,
- }).onConflictDoNothing().returning({ id: candidateMessage.id })
+ const bodyText = inboundTextContent(data.text, data.html)
+ const candidateName = `${conversation.application.candidate.firstName} ${conversation.application.candidate.lastName}`.trim()
+ const { storedMessage, shouldNotify } = await db.transaction(async (tx) => {
+ const [inserted] = await tx.insert(candidateMessage).values({
+ organizationId: conversation.organizationId,
+ conversationId: conversation.id,
+ direction: 'inbound',
+ status: 'delivered',
+ fromEmail,
+ toEmail: normalizeEmailAddress(data.to[0] ?? event.data.to[0] ?? ''),
+ subject: data.subject || event.data.subject || '(No subject)',
+ bodyText,
+ providerMessageId: data.id,
+ internetMessageId: data.message_id,
+ inReplyTo: header('in-reply-to'),
+ references: parseReferences(header('references')),
+ providerStatusAt: createdAt,
+ sentAt: createdAt,
+ deliveredAt: createdAt,
+ createdAt,
+ updatedAt: createdAt,
+ }).onConflictDoNothing().returning({ id: candidateMessage.id })
- const storedMessage = inserted ?? await db.query.candidateMessage.findFirst({
- where: eq(candidateMessage.providerMessageId, data.id),
- columns: { id: true },
+ const storedMessage = inserted ?? await tx.query.candidateMessage.findFirst({
+ where: eq(candidateMessage.providerMessageId, data.id),
+ columns: { id: true },
+ })
+ if (!storedMessage) throw new Error('Inbound message could not be persisted')
+
+ if (inserted) {
+ await tx.update(candidateConversation).set({
+ unreadCount: sql`${candidateConversation.unreadCount} + 1`,
+ lastMessageAt: createdAt,
+ updatedAt: new Date(),
+ }).where(eq(candidateConversation.id, conversation.id))
+ }
+ return { storedMessage, shouldNotify: Boolean(inserted) }
})
- if (!storedMessage) throw new Error('Inbound message could not be persisted')
- if (inserted) {
- await db.update(candidateConversation).set({
- unreadCount: sql`${candidateConversation.unreadCount} + 1`,
- lastMessageAt: createdAt,
- updatedAt: new Date(),
- }).where(eq(candidateConversation.id, conversation.id))
+ if (shouldNotify) {
+ await enqueueNotification({
+ organizationId: conversation.organizationId,
+ type: 'candidate_replied',
+ dedupeKey: `candidate_replied:${storedMessage.id}`,
+ payload: {
+ applicationId: conversation.applicationId,
+ conversationId: conversation.id,
+ candidateName,
+ jobTitle: conversation.application.job.title,
+ preview: bodyText.slice(0, 200),
+ },
+ })
}
if (data.attachments.length > 0) {
diff --git a/nuxt.config.ts b/nuxt.config.ts
index 4aa49591..b5022039 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -298,9 +298,13 @@ export default defineNuxtConfig({
tasks: true,
},
scheduledTasks: {
+ // Every minute: drain the recruiter notification outbox (instant cadence).
+ "* * * * *": ["notification-dispatch"],
// Daily at 03:00 UTC. External cron remains supported for platforms that
// suspend long-running processes or do not execute Nitro task timers.
"0 3 * * *": ["retention-cleanup"],
+ // Daily at 08:00 UTC: roll up digest-cadence notifications per recipient.
+ "0 8 * * *": ["notification-digest"],
},
routeRules: {
"/**": {
diff --git a/package-lock.json b/package-lock.json
index 17ecdf81..3b371404 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8046,9 +8046,9 @@
}
},
"node_modules/axios": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz",
- "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==",
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz",
+ "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8475,9 +8475,9 @@
"license": "MIT"
},
"node_modules/brace-expansion": {
- "version": "5.0.6",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
- "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
+ "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
@@ -9540,9 +9540,9 @@
}
},
"node_modules/dompurify": {
- "version": "3.4.11",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz",
- "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==",
+ "version": "3.4.12",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz",
+ "integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
@@ -11996,9 +11996,9 @@
}
},
"node_modules/joi": {
- "version": "18.2.1",
- "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.1.tgz",
- "integrity": "sha512-2/OKlogiESf2Nh3TFCrRjrr9z1DRHeW0I+KReF67+4J0Ns+8hBtHRmoWAZ2OFU6I5+TWLEe6sVlSdXPjHm5UbQ==",
+ "version": "18.2.3",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.3.tgz",
+ "integrity": "sha512-N5A3KTWQpPWT4ExxxPlUx7WmykGXRzhNidWhV41d6Abu9YfI2NyWCJuxdPnslJCPWtbRpSVOWSnSS6GakLM/Rg==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -12030,9 +12030,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
- "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+ "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"funding": [
{
"type": "github",
@@ -17338,9 +17338,9 @@
}
},
"node_modules/shell-quote": {
- "version": "1.8.4",
- "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz",
- "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==",
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz",
+ "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -18017,9 +18017,9 @@
}
},
"node_modules/svgo": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz",
- "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz",
+ "integrity": "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==",
"license": "MIT",
"dependencies": {
"commander": "^11.1.0",
@@ -18082,9 +18082,9 @@
}
},
"node_modules/tar": {
- "version": "7.5.16",
- "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz",
- "integrity": "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==",
+ "version": "7.5.21",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.21.tgz",
+ "integrity": "sha512-XdhtCvlMywwxpCW8YEq3lOXBJpUPTR2OHHcwLPO3HwsJqOHa2Ok/oJ7ruGzp+JrKoRPVCzJwAdEjqLW/vNRPHA==",
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
@@ -19781,14 +19781,14 @@
}
},
"node_modules/wait-on": {
- "version": "9.0.10",
- "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.0.10.tgz",
- "integrity": "sha512-rCoJEhvMr0X6alHmwc9abbrA5ZrLZFKpFQVKPNFwl2h7DapXOGdmimIHDtLOWhT4PjhZhxFEtZoQgEXbkDWdZw==",
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.1.0.tgz",
+ "integrity": "sha512-PymrLXHLBM1Ju/Xspb2ADUhbPSMvbnuNvy/mN2hWtpbJ3da0h3Ky1LqwKPG5QSVR57liyO0iUpfipYl/s5qNvA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "axios": "^1.16.0",
- "joi": "^18.2.1",
+ "axios": "^1.18.1",
+ "joi": "^18.2.3",
"lodash": "^4.18.1",
"minimist": "^1.2.8",
"rxjs": "^7.8.2"
diff --git a/package.json b/package.json
index 767afe9e..01548c41 100644
--- a/package.json
+++ b/package.json
@@ -90,11 +90,11 @@
},
"overrides": {
"fast-xml-parser": "5.8.0",
- "tar": "7.5.16",
+ "tar": "7.5.21",
"minimatch": "10.2.5",
"glob": "13.0.6",
"@isaacs/brace-expansion": "5.0.1",
- "axios": "1.16.1",
+ "axios": "1.18.1",
"@posthog/nuxt": {
"@posthog/cli": "npm:empty-npm-package@1.0.0"
},
diff --git a/railway.json b/railway.json
index d63bcbbd..0a2226c1 100644
--- a/railway.json
+++ b/railway.json
@@ -1,7 +1,7 @@
{
- "$schema": "https://railway.app/railway.schema.json",
+ "$schema": "https://railway.com/railway.schema.json",
"deploy": {
- "preDeployCommand": "npm run db:migrate && npm run db:seed",
+ "preDeployCommand": "/bin/sh -c \"npm run db:migrate && npm run db:seed\"",
"startCommand": "npm run start"
}
}
diff --git a/server/api/admin/notification-dispatch.post.ts b/server/api/admin/notification-dispatch.post.ts
new file mode 100644
index 00000000..bdb7f09d
--- /dev/null
+++ b/server/api/admin/notification-dispatch.post.ts
@@ -0,0 +1,43 @@
+/**
+ * POST /api/admin/notification-dispatch
+ *
+ * External cron and interactive admin entrypoint for the notification outbox
+ * worker. Mirrors /api/admin/retention-cleanup: a valid `x-cron-secret` runs it
+ * as a scheduled trigger, otherwise it requires an authenticated admin. Provides
+ * a timer-suspend-safe way to drain the outbox if Railway pauses the in-process
+ * Nitro cron.
+ *
+ * Pass `{ "digest": true }` to run the daily digest job instead of instant dispatch.
+ */
+import { timingSafeEqual } from 'node:crypto'
+import { z } from 'zod'
+import { runNotificationDispatch, runNotificationDigest } from '../../utils/notifications/dispatch'
+
+const bodySchema = z.object({
+ digest: z.boolean().optional().default(false),
+ batchSize: z.number().int().min(1).max(500).optional(),
+})
+
+export default defineEventHandler(async (event) => {
+ const cronSecret = getHeader(event, 'x-cron-secret')
+ let source: 'cron_endpoint' | 'interactive' = 'interactive'
+
+ if (cronSecret && env.CRON_SECRET) {
+ const supplied = Buffer.from(cronSecret)
+ const expected = Buffer.from(env.CRON_SECRET)
+ const valid = supplied.length === expected.length && timingSafeEqual(supplied, expected)
+ if (!valid) {
+ throw createError({ statusCode: 403, statusMessage: 'Invalid cron secret' })
+ }
+ source = 'cron_endpoint'
+ }
+ else {
+ // Reuse an existing high-trust permission — draining the queue is an admin op.
+ await requirePermission(event, { organization: ['update'] })
+ }
+
+ const body = await readValidatedBody(event, bodySchema.parse)
+ return body.digest
+ ? runNotificationDigest({ source, batchSize: body.batchSize })
+ : runNotificationDispatch({ source, batchSize: body.batchSize })
+})
diff --git a/server/api/applications/[id].delete.ts b/server/api/applications/[id].delete.ts
new file mode 100644
index 00000000..b1c44bd4
--- /dev/null
+++ b/server/api/applications/[id].delete.ts
@@ -0,0 +1,140 @@
+import { and, eq, inArray } from 'drizzle-orm'
+import {
+ application,
+ candidateConversation,
+ candidateMessage,
+ candidateMessageAttachment,
+ comment,
+ interview,
+ propertyValue,
+} from '../../database/schema'
+import { applicationIdParamSchema } from '../../utils/schemas/application'
+import { cancelCalendarEvent } from '../../utils/google-calendar'
+
+/**
+ * DELETE /api/applications/:id
+ *
+ * Permanently removes an application and its application-specific data. The
+ * candidate record and candidate documents are intentionally left untouched.
+ */
+export default defineEventHandler(async (event) => {
+ const session = await requirePermission(event, { application: ['delete'] })
+ const orgId = session.session.activeOrganizationId
+ const { id } = await getValidatedRouterParams(event, applicationIdParamSchema.parse)
+
+ const existing = await db.query.application.findFirst({
+ where: and(eq(application.id, id), eq(application.organizationId, orgId)),
+ columns: { id: true },
+ })
+ if (!existing) {
+ throw createError({ statusCode: 404, statusMessage: 'Application not found' })
+ }
+
+ const applicationInterviews = await db.query.interview.findMany({
+ where: and(eq(interview.applicationId, id), eq(interview.organizationId, orgId)),
+ columns: {
+ id: true,
+ status: true,
+ invitationSentAt: true,
+ googleCalendarEventId: true,
+ createdById: true,
+ },
+ })
+ const activeInvitation = applicationInterviews.find(row =>
+ row.invitationSentAt && row.status === 'scheduled',
+ )
+ if (activeInvitation) {
+ throw createError({
+ statusCode: 409,
+ statusMessage: 'Cancel scheduled interviews before deleting this application',
+ })
+ }
+
+ const conversations = await db.query.candidateConversation.findMany({
+ where: and(
+ eq(candidateConversation.applicationId, id),
+ eq(candidateConversation.organizationId, orgId),
+ ),
+ columns: { id: true },
+ })
+ const conversationIds = conversations.map(row => row.id)
+ const messages = conversationIds.length > 0
+ ? await db.query.candidateMessage.findMany({
+ where: and(
+ inArray(candidateMessage.conversationId, conversationIds),
+ eq(candidateMessage.organizationId, orgId),
+ ),
+ columns: { id: true },
+ })
+ : []
+ const messageIds = messages.map(row => row.id)
+ const attachments = messageIds.length > 0
+ ? await db.query.candidateMessageAttachment.findMany({
+ where: and(
+ inArray(candidateMessageAttachment.messageId, messageIds),
+ eq(candidateMessageAttachment.organizationId, orgId),
+ ),
+ columns: { storageKey: true },
+ })
+ : []
+
+ await db.transaction(async (tx) => {
+ await tx.delete(comment).where(and(
+ eq(comment.organizationId, orgId),
+ eq(comment.targetType, 'application'),
+ eq(comment.targetId, id),
+ ))
+ await tx.delete(propertyValue).where(and(
+ eq(propertyValue.organizationId, orgId),
+ eq(propertyValue.entityType, 'application'),
+ eq(propertyValue.entityId, id),
+ ))
+
+ const deleted = await tx.delete(application)
+ .where(and(eq(application.id, id), eq(application.organizationId, orgId)))
+ .returning({ id: application.id })
+ if (deleted.length === 0) {
+ throw createError({ statusCode: 404, statusMessage: 'Application not found' })
+ }
+ })
+
+ // The DB rows are gone; now clean up the S3 objects. Doing this after the
+ // commit avoids orphaning attachment rows against deleted storage if the
+ // transaction rolls back.
+ for (const attachment of attachments) {
+ try {
+ await deleteFromS3(attachment.storageKey)
+ }
+ catch (error) {
+ logWarn('application.attachment_delete_failed', {
+ organization_id: orgId,
+ application_id: id,
+ storage_key: attachment.storageKey,
+ error_message: error instanceof Error ? error.message : String(error),
+ })
+ }
+ }
+
+ for (const row of applicationInterviews) {
+ if (!row.googleCalendarEventId) continue
+ cancelCalendarEvent(row.createdById, row.googleCalendarEventId).catch((error) => {
+ logError('calendar.cancel_event_on_application_delete_failed', {
+ organization_id: orgId,
+ application_id: id,
+ event_id: row.googleCalendarEventId,
+ error_message: error instanceof Error ? error.message : String(error),
+ })
+ })
+ }
+
+ recordActivity({
+ organizationId: orgId,
+ actorId: session.user.id,
+ action: 'deleted',
+ resourceType: 'application',
+ resourceId: id,
+ })
+
+ setResponseStatus(event, 204)
+ return null
+})
diff --git a/server/api/applications/[id].get.ts b/server/api/applications/[id].get.ts
index aa5ea2e9..46f002e4 100644
--- a/server/api/applications/[id].get.ts
+++ b/server/api/applications/[id].get.ts
@@ -1,5 +1,5 @@
-import { eq, and } from 'drizzle-orm'
-import { application } from '../../database/schema'
+import { eq, and, inArray, isNull } from 'drizzle-orm'
+import { application, candidate } from '../../database/schema'
import { applicationIdParamSchema } from '../../utils/schemas/application'
import { loadPropertyEntriesForEntity } from '../../utils/properties'
@@ -12,9 +12,17 @@ export default defineEventHandler(async (event) => {
const orgId = session.session.activeOrganizationId
const { id } = await getValidatedRouterParams(event, applicationIdParamSchema.parse)
+ const activeCandidateIds = db.select({ id: candidate.id }).from(candidate).where(and(
+ eq(candidate.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ))
const result = await db.query.application.findFirst({
- where: and(eq(application.id, id), eq(application.organizationId, orgId)),
+ where: and(
+ eq(application.id, id),
+ eq(application.organizationId, orgId),
+ inArray(application.candidateId, activeCandidateIds),
+ ),
with: {
candidate: {
columns: { id: true, firstName: true, lastName: true, email: true, phone: true },
diff --git a/server/api/applications/[id].patch.ts b/server/api/applications/[id].patch.ts
index 37abbf0b..cd3580c6 100644
--- a/server/api/applications/[id].patch.ts
+++ b/server/api/applications/[id].patch.ts
@@ -1,5 +1,5 @@
-import { eq, and } from 'drizzle-orm'
-import { application } from '../../database/schema'
+import { eq, and, inArray, isNull } from 'drizzle-orm'
+import { application, candidate } from '../../database/schema'
import { applicationIdParamSchema, updateApplicationSchema, APPLICATION_STATUS_TRANSITIONS } from '../../utils/schemas/application'
/**
@@ -12,10 +12,18 @@ export default defineEventHandler(async (event) => {
const { id } = await getValidatedRouterParams(event, applicationIdParamSchema.parse)
const body = await readValidatedBody(event, updateApplicationSchema.parse)
+ const activeCandidateIds = db.select({ id: candidate.id }).from(candidate).where(and(
+ eq(candidate.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ))
// Fetch current application to validate status transition
const current = await db.query.application.findFirst({
- where: and(eq(application.id, id), eq(application.organizationId, orgId)),
+ where: and(
+ eq(application.id, id),
+ eq(application.organizationId, orgId),
+ inArray(application.candidateId, activeCandidateIds),
+ ),
columns: { id: true, status: true },
})
@@ -36,7 +44,11 @@ export default defineEventHandler(async (event) => {
const [updated] = await db.update(application)
.set({ ...body, updatedAt: new Date() })
- .where(and(eq(application.id, id), eq(application.organizationId, orgId)))
+ .where(and(
+ eq(application.id, id),
+ eq(application.organizationId, orgId),
+ inArray(application.candidateId, activeCandidateIds),
+ ))
.returning({
id: application.id,
candidateId: application.candidateId,
diff --git a/server/api/applications/[id]/analyze.post.ts b/server/api/applications/[id]/analyze.post.ts
index 25850486..09b33425 100644
--- a/server/api/applications/[id]/analyze.post.ts
+++ b/server/api/applications/[id]/analyze.post.ts
@@ -1,6 +1,6 @@
-import { eq, and } from 'drizzle-orm'
+import { eq, and, inArray, isNull } from 'drizzle-orm'
import {
- application, scoringCriterion, criterionScore,
+ application, candidate, scoringCriterion, criterionScore,
analysisRun, document,
} from '../../../database/schema'
import { scoreApplication, computeCompositeScore, hasScorableCandidateMaterial } from '../../../utils/ai/scoring'
@@ -35,10 +35,18 @@ export default defineEventHandler(async (event) => {
// Body is optional — GET-style "just run with defaults" stays valid.
const body = await readBody(event).catch(() => null)
const parsedBody = body ? bodySchema.parse(body) : null
+ const activeCandidateIds = db.select({ id: candidate.id }).from(candidate).where(and(
+ eq(candidate.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ))
// Fetch application with candidate, job, and documents
const app = await db.query.application.findFirst({
- where: and(eq(application.id, applicationId), eq(application.organizationId, orgId)),
+ where: and(
+ eq(application.id, applicationId),
+ eq(application.organizationId, orgId),
+ inArray(application.candidateId, activeCandidateIds),
+ ),
with: {
candidate: {
columns: { id: true, firstName: true, lastName: true },
diff --git a/server/api/applications/[id]/properties/[propId].put.ts b/server/api/applications/[id]/properties/[propId].put.ts
index 46337b64..218ca2a8 100644
--- a/server/api/applications/[id]/properties/[propId].put.ts
+++ b/server/api/applications/[id]/properties/[propId].put.ts
@@ -1,6 +1,6 @@
-import { and, eq } from 'drizzle-orm'
+import { and, eq, inArray, isNull } from 'drizzle-orm'
import { z } from 'zod'
-import { application, propertyDefinition, propertyValue } from '../../../../database/schema'
+import { application, candidate, propertyDefinition, propertyValue } from '../../../../database/schema'
import {
setPropertyValueSchema,
validateValueForType,
@@ -22,10 +22,18 @@ export default defineEventHandler(async (event) => {
const { id, propId } = await getValidatedRouterParams(event, paramsSchema.parse)
const { value } = await readValidatedBody(event, setPropertyValueSchema.parse)
+ const activeCandidateIds = db.select({ id: candidate.id }).from(candidate).where(and(
+ eq(candidate.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ))
// Verify entity belongs to org
const app = await db.query.application.findFirst({
- where: and(eq(application.id, id), eq(application.organizationId, orgId)),
+ where: and(
+ eq(application.id, id),
+ eq(application.organizationId, orgId),
+ inArray(application.candidateId, activeCandidateIds),
+ ),
columns: { id: true, jobId: true },
})
if (!app) throw createError({ statusCode: 404, statusMessage: 'Application not found' })
diff --git a/server/api/applications/[id]/scores.get.ts b/server/api/applications/[id]/scores.get.ts
index ad8ba026..bd27deee 100644
--- a/server/api/applications/[id]/scores.get.ts
+++ b/server/api/applications/[id]/scores.get.ts
@@ -1,5 +1,5 @@
-import { eq, and, desc } from 'drizzle-orm'
-import { application, criterionScore, analysisRun, scoringCriterion } from '../../../database/schema'
+import { eq, and, desc, inArray, isNull } from 'drizzle-orm'
+import { application, candidate, criterionScore, analysisRun, scoringCriterion } from '../../../database/schema'
import { z } from 'zod'
const paramsSchema = z.object({ id: z.string().min(1) })
@@ -13,10 +13,18 @@ export default defineEventHandler(async (event) => {
const session = await requirePermission(event, { scoring: ['read'] })
const orgId = session.session.activeOrganizationId
const { id: applicationId } = await getValidatedRouterParams(event, paramsSchema.parse)
+ const activeCandidateIds = db.select({ id: candidate.id }).from(candidate).where(and(
+ eq(candidate.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ))
// Verify application belongs to org
const app = await db.query.application.findFirst({
- where: and(eq(application.id, applicationId), eq(application.organizationId, orgId)),
+ where: and(
+ eq(application.id, applicationId),
+ eq(application.organizationId, orgId),
+ inArray(application.candidateId, activeCandidateIds),
+ ),
columns: { id: true, score: true, jobId: true },
})
if (!app) {
diff --git a/server/api/applications/index.get.ts b/server/api/applications/index.get.ts
index 23614697..e03d39a9 100644
--- a/server/api/applications/index.get.ts
+++ b/server/api/applications/index.get.ts
@@ -30,7 +30,10 @@ export default defineEventHandler(async (event) => {
const query = await getValidatedQuery(event, applicationQuerySchema.parse)
const offset = (query.page - 1) * query.limit
- const conditions = [eq(application.organizationId, orgId)]
+ const conditions = [
+ eq(application.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ]
if (query.jobId) {
conditions.push(eq(application.jobId, query.jobId))
@@ -102,7 +105,12 @@ export default defineEventHandler(async (event) => {
? db
.select({ status: application.status, count: count() })
.from(application)
- .where(and(eq(application.organizationId, orgId), eq(application.jobId, query.jobId)))
+ .innerJoin(candidate, eq(candidate.id, application.candidateId))
+ .where(and(
+ eq(application.organizationId, orgId),
+ eq(application.jobId, query.jobId),
+ isNull(candidate.quarantinedAt),
+ ))
.groupBy(application.status)
: Promise.resolve([])
diff --git a/server/api/dashboard/stats.get.ts b/server/api/dashboard/stats.get.ts
index f0d7875b..4b3d0dfc 100644
--- a/server/api/dashboard/stats.get.ts
+++ b/server/api/dashboard/stats.get.ts
@@ -1,4 +1,4 @@
-import { eq, and, desc, sql, count } from 'drizzle-orm'
+import { eq, and, desc, sql, count, inArray, isNull } from 'drizzle-orm'
import { application, candidate, job } from '../../database/schema'
/**
@@ -13,6 +13,14 @@ import { application, candidate, job } from '../../database/schema'
export default defineEventHandler(async (event) => {
const session = await requirePermission(event, { job: ['read'], candidate: ['read'], application: ['read'] })
const orgId = session.session.activeOrganizationId
+ const activeCandidateIds = db.select({ id: candidate.id }).from(candidate).where(and(
+ eq(candidate.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ))
+ const activeApplicationCondition = and(
+ eq(application.organizationId, orgId),
+ inArray(application.candidateId, activeCandidateIds),
+ )
// ─────────────────────────────────────────────
// Run all queries in parallel for performance
@@ -31,13 +39,13 @@ export default defineEventHandler(async (event) => {
db.$count(job, and(eq(job.organizationId, orgId), eq(job.status, 'open'))),
// 2. Total candidates
- db.$count(candidate, eq(candidate.organizationId, orgId)),
+ db.$count(candidate, and(eq(candidate.organizationId, orgId), isNull(candidate.quarantinedAt))),
// 3. Total applications
- db.$count(application, eq(application.organizationId, orgId)),
+ db.$count(application, activeApplicationCondition),
// 4. New (unreviewed) applications
- db.$count(application, and(eq(application.organizationId, orgId), eq(application.status, 'new'))),
+ db.$count(application, and(activeApplicationCondition, eq(application.status, 'new'))),
// 5. Pipeline breakdown — application count per status
db
@@ -46,7 +54,7 @@ export default defineEventHandler(async (event) => {
count: count().as('count'),
})
.from(application)
- .where(eq(application.organizationId, orgId))
+ .where(activeApplicationCondition)
.groupBy(application.status),
// 6. Jobs by status
@@ -75,7 +83,7 @@ export default defineEventHandler(async (event) => {
.from(application)
.innerJoin(candidate, eq(candidate.id, application.candidateId))
.innerJoin(job, eq(job.id, application.jobId))
- .where(eq(application.organizationId, orgId))
+ .where(and(eq(application.organizationId, orgId), isNull(candidate.quarantinedAt)))
.orderBy(desc(application.createdAt))
.limit(10),
@@ -96,7 +104,10 @@ export default defineEventHandler(async (event) => {
rejectedCount: sql`count(case when ${application.status} = 'rejected' then 1 end)`.as('rejected_count'),
})
.from(job)
- .leftJoin(application, eq(application.jobId, job.id))
+ .leftJoin(application, and(
+ eq(application.jobId, job.id),
+ inArray(application.candidateId, activeCandidateIds),
+ ))
.where(and(eq(job.organizationId, orgId), eq(job.status, 'open')))
.groupBy(job.id)
.orderBy(sql`count(${application.id}) desc`)
diff --git a/server/api/interviews/index.get.ts b/server/api/interviews/index.get.ts
index f719ce63..83e3229c 100644
--- a/server/api/interviews/index.get.ts
+++ b/server/api/interviews/index.get.ts
@@ -1,4 +1,4 @@
-import { and, count, desc, eq, gte, lte } from 'drizzle-orm'
+import { and, count, desc, eq, gte, isNull, lte } from 'drizzle-orm'
import { interview, application, candidate, job } from '../../database/schema'
import { interviewQuerySchema } from '../../utils/schemas/interview'
@@ -8,7 +8,10 @@ export default defineEventHandler(async (event) => {
const query = await getValidatedQuery(event, interviewQuerySchema.parse)
- const conditions = [eq(interview.organizationId, orgId)]
+ const conditions = [
+ eq(interview.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ]
if (query.applicationId) {
conditions.push(eq(interview.applicationId, query.applicationId))
diff --git a/server/api/jobs/[id].get.ts b/server/api/jobs/[id].get.ts
index 5596eb75..a5f805e2 100644
--- a/server/api/jobs/[id].get.ts
+++ b/server/api/jobs/[id].get.ts
@@ -1,5 +1,5 @@
-import { eq, and } from 'drizzle-orm'
-import { job } from '../../database/schema'
+import { eq, and, inArray, isNull } from 'drizzle-orm'
+import { application, candidate, job } from '../../database/schema'
import { idParamSchema } from '../../utils/schemas/job'
export default defineEventHandler(async (event) => {
@@ -7,6 +7,10 @@ export default defineEventHandler(async (event) => {
const orgId = session.session.activeOrganizationId
const { id } = await getValidatedRouterParams(event, idParamSchema.parse)
+ const activeCandidateIds = db.select({ id: candidate.id }).from(candidate).where(and(
+ eq(candidate.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ))
const result = await db.query.job.findFirst({
where: and(eq(job.id, id), eq(job.organizationId, orgId)),
@@ -37,6 +41,7 @@ export default defineEventHandler(async (event) => {
with: {
applications: {
columns: { id: true, candidateId: true, status: true, createdAt: true },
+ where: inArray(application.candidateId, activeCandidateIds),
limit: 100,
},
},
diff --git a/server/api/jobs/[id]/analyze-all.post.ts b/server/api/jobs/[id]/analyze-all.post.ts
index cf14c1e2..6f34e274 100644
--- a/server/api/jobs/[id]/analyze-all.post.ts
+++ b/server/api/jobs/[id]/analyze-all.post.ts
@@ -1,5 +1,5 @@
-import { eq, and, isNull } from 'drizzle-orm'
-import { application, job } from '../../../database/schema'
+import { eq, and, inArray, isNull } from 'drizzle-orm'
+import { application, candidate, job } from '../../../database/schema'
import { z } from 'zod'
const paramsSchema = z.object({ id: z.string().min(1) })
@@ -24,6 +24,10 @@ export default defineEventHandler(async (event) => {
if (!jobRecord) {
throw createError({ statusCode: 404, statusMessage: 'Job not found' })
}
+ const activeCandidateIds = db.select({ id: candidate.id }).from(candidate).where(and(
+ eq(candidate.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ))
// Find all applications without scores
const unscoredApps = await db.select({
@@ -34,6 +38,7 @@ export default defineEventHandler(async (event) => {
eq(application.jobId, jobId),
eq(application.organizationId, orgId),
isNull(application.score),
+ inArray(application.candidateId, activeCandidateIds),
))
return {
diff --git a/server/api/jobs/index.get.ts b/server/api/jobs/index.get.ts
index 7586c450..51ee7f5f 100644
--- a/server/api/jobs/index.get.ts
+++ b/server/api/jobs/index.get.ts
@@ -1,5 +1,5 @@
-import { eq, and, desc, count, inArray } from 'drizzle-orm'
-import { job, application } from '../../database/schema'
+import { eq, and, desc, count, inArray, isNull } from 'drizzle-orm'
+import { job, application, candidate } from '../../database/schema'
import { jobQuerySchema } from '../../utils/schemas/job'
interface PipelineCounts {
@@ -49,6 +49,10 @@ export default defineEventHandler(async (event) => {
let pipelineMap: Record = {}
if (jobIds.length > 0) {
+ const activeCandidateIds = db.select({ id: candidate.id }).from(candidate).where(and(
+ eq(candidate.organizationId, orgId),
+ isNull(candidate.quarantinedAt),
+ ))
const pipelineRows = await db
.select({
jobId: application.jobId,
@@ -59,6 +63,7 @@ export default defineEventHandler(async (event) => {
.where(and(
eq(application.organizationId, orgId),
inArray(application.jobId, jobIds),
+ inArray(application.candidateId, activeCandidateIds),
))
.groupBy(application.jobId, application.status)
diff --git a/server/api/notification-preferences/index.get.ts b/server/api/notification-preferences/index.get.ts
new file mode 100644
index 00000000..40c47ac7
--- /dev/null
+++ b/server/api/notification-preferences/index.get.ts
@@ -0,0 +1,39 @@
+import { and, eq } from 'drizzle-orm'
+import { notificationPreference } from '../../database/schema'
+import {
+ DEFAULT_CHANNEL_MODE,
+ NOTIFICATION_TYPES,
+ normalizeNotificationChannelMode,
+} from '~~/shared/notifications'
+import { isDemoAccountEmail } from '../../utils/demoOrg'
+
+/**
+ * GET /api/notification-preferences
+ * Returns the current user's per-event notification preferences for the active
+ * org, with unset events filled in from the sensible defaults.
+ */
+export default defineEventHandler(async (event) => {
+ const session = await requireAuth(event)
+ const orgId = session.session.activeOrganizationId
+ const userId = session.user.id
+
+ if (isDemoAccountEmail(session.user.email)) {
+ return NOTIFICATION_TYPES.map(type => ({ type, channelMode: 'off' as const }))
+ }
+
+ const rows = await db.select({
+ type: notificationPreference.type,
+ channelMode: notificationPreference.channelMode,
+ })
+ .from(notificationPreference)
+ .where(and(
+ eq(notificationPreference.userId, userId),
+ eq(notificationPreference.organizationId, orgId),
+ ))
+ const byType = new Map(rows.map(r => [r.type, r.channelMode]))
+
+ return NOTIFICATION_TYPES.map(type => ({
+ type,
+ channelMode: normalizeNotificationChannelMode(type, byType.get(type) ?? DEFAULT_CHANNEL_MODE[type]),
+ }))
+})
diff --git a/server/api/notification-preferences/index.patch.ts b/server/api/notification-preferences/index.patch.ts
new file mode 100644
index 00000000..51c53905
--- /dev/null
+++ b/server/api/notification-preferences/index.patch.ts
@@ -0,0 +1,69 @@
+import { and, eq } from 'drizzle-orm'
+import { z } from 'zod'
+import { notificationPreference } from '../../database/schema'
+import {
+ DEFAULT_CHANNEL_MODE,
+ NOTIFICATION_TYPES,
+ isNotificationChannelModeAllowed,
+ normalizeNotificationChannelMode,
+} from '~~/shared/notifications'
+import { isDemoAccountEmail } from '../../utils/demoOrg'
+
+const bodySchema = z.object({
+ preferences: z.array(z.object({
+ type: z.enum(NOTIFICATION_TYPES),
+ channelMode: z.enum(['instant', 'digest', 'off']),
+ })).min(1),
+}).superRefine(({ preferences }, ctx) => {
+ preferences.forEach((preference, index) => {
+ if (!isNotificationChannelModeAllowed(preference.type, preference.channelMode)) {
+ ctx.addIssue({
+ code: 'custom',
+ path: ['preferences', index, 'channelMode'],
+ message: `${preference.channelMode} delivery is not available for ${preference.type}`,
+ })
+ }
+ })
+})
+
+/**
+ * PATCH /api/notification-preferences
+ * Upsert one or more of the current user's per-event preferences for the active
+ * org, then return the full (defaults-filled) set — same shape as the GET route.
+ */
+export default defineEventHandler(async (event) => {
+ const session = await requireAuth(event)
+ const orgId = session.session.activeOrganizationId
+ const userId = session.user.id
+
+ if (isDemoAccountEmail(session.user.email)) {
+ return NOTIFICATION_TYPES.map(type => ({ type, channelMode: 'off' as const }))
+ }
+
+ const { preferences } = await readValidatedBody(event, bodySchema.parse)
+
+ for (const pref of preferences) {
+ await db.insert(notificationPreference)
+ .values({ userId, organizationId: orgId, type: pref.type, channelMode: pref.channelMode })
+ .onConflictDoUpdate({
+ target: [notificationPreference.userId, notificationPreference.organizationId, notificationPreference.type],
+ set: { channelMode: pref.channelMode, updatedAt: new Date() },
+ })
+ }
+
+ const rows = await db.select({
+ type: notificationPreference.type,
+ channelMode: notificationPreference.channelMode,
+ })
+ .from(notificationPreference)
+ .where(and(
+ eq(notificationPreference.userId, userId),
+ eq(notificationPreference.organizationId, orgId),
+ ))
+ const byType = new Map(rows.map(r => [r.type, r.channelMode]))
+
+ return NOTIFICATION_TYPES.map(type => ({
+ type,
+ channelMode: normalizeNotificationChannelMode(type, byType.get(type) ?? DEFAULT_CHANNEL_MODE[type]),
+ }))
+})
diff --git a/server/api/onboarding-survey/index.get.ts b/server/api/onboarding-survey/index.get.ts
index 936b9a6f..121d469c 100644
--- a/server/api/onboarding-survey/index.get.ts
+++ b/server/api/onboarding-survey/index.get.ts
@@ -12,6 +12,12 @@ export default defineEventHandler(async (event) => {
const session = await requireAuth(event)
const userId = session.user.id
+ // The shared demo account never gets the onboarding survey (nor its
+ // re-prompt banner) — it's a read-only showcase, not a real signup.
+ if (isDemoAccountEmail(session.user.email)) {
+ return { completed: true }
+ }
+
const row = await db.query.onboardingSurveyResponse.findFirst({
where: eq(onboardingSurveyResponse.userId, userId),
columns: { completedAt: true },
diff --git a/server/api/public/interviews/respond.post.ts b/server/api/public/interviews/respond.post.ts
index 33fbaf08..ca78d112 100644
--- a/server/api/public/interviews/respond.post.ts
+++ b/server/api/public/interviews/respond.post.ts
@@ -3,6 +3,7 @@ import { z } from 'zod'
import { application, candidateConversation, candidateMessage, interview } from '../../../database/schema'
import { normalizeEmailAddress, replySubject } from '../../../../ee/server/utils/candidate-messaging'
import { verifyInterviewToken } from '../../../utils/interview-token'
+import { enqueueInterviewResponseNotification } from '../../../utils/notifications/interview-response'
const respondBodySchema = z.object({
token: z.string().min(1, 'Token is required'),
@@ -54,7 +55,10 @@ export default defineEventHandler(async (event) => {
const app = await db.query.application.findFirst({
where: eq(application.id, interviewRecord.applicationId),
- with: { candidate: { columns: { email: true } } },
+ with: {
+ candidate: { columns: { email: true, firstName: true, lastName: true } },
+ job: { columns: { title: true } },
+ },
})
const conversation = await db.query.candidateConversation.findFirst({
where: and(
@@ -76,7 +80,7 @@ export default defineEventHandler(async (event) => {
? 'Requested another time'
: 'Declined the interview'
- const updated = await db.transaction(async (tx) => {
+ const { updated, shouldNotify } = await db.transaction(async (tx) => {
// Serialize concurrent responses for this interview. Two flips arriving at
// once must not both pass the change check below and double-insert a
// response message (or double-count the unread badge).
@@ -91,9 +95,12 @@ export default defineEventHandler(async (event) => {
if (!current || current.candidateResponse === action) {
return {
- id: payload.id,
- candidateResponse: current?.candidateResponse ?? action,
- candidateRespondedAt: current?.candidateRespondedAt ?? respondedAt,
+ updated: {
+ id: payload.id,
+ candidateResponse: current?.candidateResponse ?? action,
+ candidateRespondedAt: current?.candidateRespondedAt ?? respondedAt,
+ },
+ shouldNotify: false,
}
}
@@ -146,9 +153,20 @@ export default defineEventHandler(async (event) => {
}).where(eq(candidateConversation.id, conversation.id))
}
}
- return result
+ return { updated: result, shouldNotify: Boolean(result) }
})
+ if (shouldNotify && app?.candidate && app.job) {
+ await enqueueInterviewResponseNotification({
+ organizationId: interviewRecord.organizationId,
+ interviewId: interviewRecord.id,
+ candidateName: `${app.candidate.firstName} ${app.candidate.lastName}`.trim() || app.candidate.email,
+ jobTitle: app.job.title,
+ interviewTitle: interviewRecord.title,
+ response: action,
+ })
+ }
+
return {
success: true,
interviewId: updated?.id,
diff --git a/server/api/public/jobs/[slug]/apply.post.ts b/server/api/public/jobs/[slug]/apply.post.ts
index 1c608489..6787c5ff 100644
--- a/server/api/public/jobs/[slug]/apply.post.ts
+++ b/server/api/public/jobs/[slug]/apply.post.ts
@@ -13,6 +13,7 @@ import {
sanitizeFilename,
} from '../../../../utils/schemas/document'
import { restoreCandidateForPublicApplication } from '../../../../utils/candidate-retention'
+import { enqueueNotification } from '../../../../utils/notifications/enqueue'
/** Rate limit: max 5 applications per IP per 15 minutes */
const applyRateLimit = createRateLimiter({
@@ -182,6 +183,7 @@ export default defineEventHandler(async (event) => {
columns: {
id: true,
organizationId: true,
+ title: true,
phoneRequirement: true,
requireResume: true,
requireCoverLetter: true,
@@ -417,13 +419,28 @@ export default defineEventHandler(async (event) => {
// 8. Create application
// ─────────────────────────────────────────────
- const [newApplication] = await db.insert(application).values({
- organizationId: orgId,
- candidateId,
- jobId,
- status: 'new',
- coverLetterText: coverLetterText || null,
- }).returning({ id: application.id })
+ const [newApplication] = await db.transaction(async (tx) => {
+ return tx.insert(application).values({
+ organizationId: orgId,
+ candidateId,
+ jobId,
+ status: 'new',
+ coverLetterText: coverLetterText || null,
+ }).returning({ id: application.id })
+ })
+
+ if (newApplication) {
+ await enqueueNotification({
+ organizationId: orgId,
+ type: 'application_created',
+ dedupeKey: `application_created:${newApplication.id}`,
+ payload: {
+ applicationId: newApplication.id,
+ candidateName: `${firstName} ${lastName}`.trim(),
+ jobTitle: existingJob.title,
+ },
+ })
+ }
// ─────────────────────────────────────────────
// 8b. Record source attribution
diff --git a/server/api/tracking-links/[id]/stats.get.ts b/server/api/tracking-links/[id]/stats.get.ts
index 909d3709..e43044aa 100644
--- a/server/api/tracking-links/[id]/stats.get.ts
+++ b/server/api/tracking-links/[id]/stats.get.ts
@@ -1,4 +1,4 @@
-import { eq, and, sql, count, gte, lte, desc } from 'drizzle-orm'
+import { eq, and, sql, count, gte, isNull, lte, desc } from 'drizzle-orm'
import { applicationSource, application, trackingLink, job, candidate } from '../../../database/schema'
import { trackingLinkIdSchema, sourceStatsQuerySchema } from '../../../utils/schemas/trackingLink'
@@ -38,7 +38,10 @@ export default defineEventHandler(async (event) => {
}
// ─── Date conditions ──────────────────────
- const dateConditions = [eq(applicationSource.trackingLinkId, id)]
+ const dateConditions = [
+ eq(applicationSource.trackingLinkId, id),
+ isNull(candidate.quarantinedAt),
+ ]
if (query.from) {
dateConditions.push(gte(applicationSource.createdAt, new Date(query.from)))
}
@@ -64,6 +67,7 @@ export default defineEventHandler(async (event) => {
})
.from(applicationSource)
.innerJoin(application, eq(application.id, applicationSource.applicationId))
+ .innerJoin(candidate, eq(candidate.id, application.candidateId))
.where(whereClause)
.groupBy(application.status),
@@ -75,6 +79,7 @@ export default defineEventHandler(async (event) => {
})
.from(applicationSource)
.innerJoin(application, eq(application.id, applicationSource.applicationId))
+ .innerJoin(candidate, eq(candidate.id, application.candidateId))
.where(whereClause)
.groupBy(sql`date_trunc('day', ${applicationSource.createdAt})::date`)
.orderBy(sql`date_trunc('day', ${applicationSource.createdAt})::date`),
@@ -114,6 +119,7 @@ export default defineEventHandler(async (event) => {
})
.from(applicationSource)
.innerJoin(application, eq(application.id, applicationSource.applicationId))
+ .innerJoin(candidate, eq(candidate.id, application.candidateId))
.where(and(
...dateConditions,
sql`${applicationSource.referrerDomain} IS NOT NULL`,
@@ -123,7 +129,16 @@ export default defineEventHandler(async (event) => {
.limit(10),
// 5. Total attributed count
- db.$count(applicationSource, eq(applicationSource.trackingLinkId, id)),
+ db
+ .select({ count: count() })
+ .from(applicationSource)
+ .innerJoin(application, eq(application.id, applicationSource.applicationId))
+ .innerJoin(candidate, eq(candidate.id, application.candidateId))
+ .where(and(
+ eq(applicationSource.trackingLinkId, id),
+ isNull(candidate.quarantinedAt),
+ ))
+ .then(rows => Number(rows[0]?.count ?? 0)),
])
// ─── Build funnel map ─────────────────────
@@ -134,7 +149,7 @@ export default defineEventHandler(async (event) => {
// ─── Conversion rate ──────────────────────
const cvr = link.clickCount > 0
- ? Math.round((link.applicationCount / link.clickCount) * 100)
+ ? Math.round((totalAttributed / link.clickCount) * 100)
: 0
return {
@@ -151,7 +166,7 @@ export default defineEventHandler(async (event) => {
utmTerm: link.utmTerm,
utmContent: link.utmContent,
clickCount: link.clickCount,
- applicationCount: link.applicationCount,
+ applicationCount: totalAttributed,
isActive: link.isActive,
createdAt: link.createdAt,
cvr,
diff --git a/server/database/migrations/0048_brave_human_torch.sql b/server/database/migrations/0048_brave_human_torch.sql
new file mode 100644
index 00000000..2930f140
--- /dev/null
+++ b/server/database/migrations/0048_brave_human_torch.sql
@@ -0,0 +1,66 @@
+DO $migration$
+BEGIN
+ -- This PR previously deployed the same schema as migrations 0045 and 0046.
+ -- Skip the renumbered migration when both legacy entries are in Drizzle's ledger.
+ IF EXISTS (
+ SELECT 1 FROM "drizzle"."__drizzle_migrations" WHERE "created_at" = 1784529680195
+ ) AND EXISTS (
+ SELECT 1 FROM "drizzle"."__drizzle_migrations" WHERE "created_at" = 1784532824839
+ ) THEN
+ RETURN;
+ END IF;
+
+CREATE TYPE "public"."email_suppression_reason" AS ENUM('bounce', 'complaint');
+CREATE TYPE "public"."notification_cadence" AS ENUM('instant', 'digest');
+CREATE TYPE "public"."notification_channel_mode" AS ENUM('instant', 'digest', 'off');
+CREATE TYPE "public"."notification_outbox_status" AS ENUM('pending', 'sent', 'skipped', 'dead');
+CREATE TYPE "public"."notification_type" AS ENUM('candidate_replied', 'application_created', 'analysis_completed');
+CREATE TABLE "email_suppression" (
+ "id" text PRIMARY KEY NOT NULL,
+ "email" text NOT NULL,
+ "reason" "email_suppression_reason" NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL
+);
+CREATE TABLE "notification_outbox" (
+ "id" text PRIMARY KEY NOT NULL,
+ "organization_id" text NOT NULL,
+ "recipient_user_id" text NOT NULL,
+ "recipient_email" text NOT NULL,
+ "type" "notification_type" NOT NULL,
+ "cadence" "notification_cadence" NOT NULL,
+ "dedupe_key" text NOT NULL,
+ "payload" jsonb NOT NULL,
+ "status" "notification_outbox_status" DEFAULT 'pending' NOT NULL,
+ "attempts" integer DEFAULT 0 NOT NULL,
+ "next_attempt_at" timestamp DEFAULT now() NOT NULL,
+ "digest_bucket" text,
+ "provider_message_id" text,
+ "sent_at" timestamp,
+ "failed_at" timestamp,
+ "last_error" text,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL
+);
+CREATE TABLE "notification_preference" (
+ "id" text PRIMARY KEY NOT NULL,
+ "user_id" text NOT NULL,
+ "organization_id" text NOT NULL,
+ "type" "notification_type" NOT NULL,
+ "channel_mode" "notification_channel_mode" NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL
+);
+ALTER TABLE "notification_outbox" ADD CONSTRAINT "notification_outbox_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;
+ALTER TABLE "notification_outbox" ADD CONSTRAINT "notification_outbox_recipient_user_id_user_id_fk" FOREIGN KEY ("recipient_user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
+ALTER TABLE "notification_preference" ADD CONSTRAINT "notification_preference_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
+ALTER TABLE "notification_preference" ADD CONSTRAINT "notification_preference_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;
+CREATE UNIQUE INDEX "email_suppression_email_idx" ON "email_suppression" USING btree (lower("email"));
+CREATE UNIQUE INDEX "notification_outbox_dedupe_key_idx" ON "notification_outbox" USING btree ("dedupe_key");
+CREATE INDEX "notification_outbox_pull_idx" ON "notification_outbox" USING btree ("cadence","next_attempt_at") WHERE "notification_outbox"."status" = 'pending';
+CREATE UNIQUE INDEX "notification_outbox_provider_id_idx" ON "notification_outbox" USING btree ("provider_message_id") WHERE "notification_outbox"."provider_message_id" is not null;
+CREATE INDEX "notification_outbox_organization_id_idx" ON "notification_outbox" USING btree ("organization_id");
+CREATE INDEX "notification_outbox_digest_idx" ON "notification_outbox" USING btree ("organization_id","recipient_user_id","digest_bucket");
+CREATE UNIQUE INDEX "notification_preference_user_org_type_idx" ON "notification_preference" USING btree ("user_id","organization_id","type");
+CREATE INDEX "notification_preference_organization_id_idx" ON "notification_preference" USING btree ("organization_id");
+END
+$migration$;
diff --git a/server/database/migrations/0049_friendly_blacklash.sql b/server/database/migrations/0049_friendly_blacklash.sql
new file mode 100644
index 00000000..c30bc382
--- /dev/null
+++ b/server/database/migrations/0049_friendly_blacklash.sql
@@ -0,0 +1 @@
+ALTER TYPE "public"."notification_type" ADD VALUE 'interview_response';
\ No newline at end of file
diff --git a/server/database/migrations/0050_numerous_red_wolf.sql b/server/database/migrations/0050_numerous_red_wolf.sql
new file mode 100644
index 00000000..e7e25fd6
--- /dev/null
+++ b/server/database/migrations/0050_numerous_red_wolf.sql
@@ -0,0 +1,14 @@
+DELETE FROM "notification_outbox" WHERE "type" = 'analysis_completed';--> statement-breakpoint
+DELETE FROM "notification_preference" WHERE "type" = 'analysis_completed';--> statement-breakpoint
+UPDATE "notification_preference"
+SET "channel_mode" = 'digest', "updated_at" = now()
+WHERE "type" = 'application_created' AND "channel_mode" = 'instant';--> statement-breakpoint
+UPDATE "notification_outbox"
+SET "status" = 'skipped', "updated_at" = now(), "last_error" = 'Instant new-application notifications disabled'
+WHERE "type" = 'application_created' AND "cadence" = 'instant' AND "status" = 'pending';--> statement-breakpoint
+ALTER TABLE "notification_outbox" ALTER COLUMN "type" SET DATA TYPE text;--> statement-breakpoint
+ALTER TABLE "notification_preference" ALTER COLUMN "type" SET DATA TYPE text;--> statement-breakpoint
+DROP TYPE "public"."notification_type";--> statement-breakpoint
+CREATE TYPE "public"."notification_type" AS ENUM('candidate_replied', 'application_created', 'interview_response');--> statement-breakpoint
+ALTER TABLE "notification_outbox" ALTER COLUMN "type" SET DATA TYPE "public"."notification_type" USING "type"::"public"."notification_type";--> statement-breakpoint
+ALTER TABLE "notification_preference" ALTER COLUMN "type" SET DATA TYPE "public"."notification_type" USING "type"::"public"."notification_type";
diff --git a/server/database/migrations/meta/0048_snapshot.json b/server/database/migrations/meta/0048_snapshot.json
new file mode 100644
index 00000000..7c5c1968
--- /dev/null
+++ b/server/database/migrations/meta/0048_snapshot.json
@@ -0,0 +1,7849 @@
+{
+ "id": "5af28487-0a51-4974-8336-069747b058ef",
+ "prevId": "31ad7fc9-5960-4ed3-86d9-0e8c7f904c75",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "account_user_id_idx": {
+ "name": "account_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invitation": {
+ "name": "invitation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "inviter_id": {
+ "name": "inviter_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "invitation_organization_id_idx": {
+ "name": "invitation_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invitation_email_idx": {
+ "name": "invitation_email_idx",
+ "columns": [
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invitation_inviter_id_user_id_fk": {
+ "name": "invitation_inviter_id_user_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "user",
+ "columnsFrom": [
+ "inviter_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invitation_organization_id_organization_id_fk": {
+ "name": "invitation_organization_id_organization_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.member": {
+ "name": "member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'member'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "member_user_id_idx": {
+ "name": "member_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "member_organization_id_idx": {
+ "name": "member_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "member_user_org_unique_idx": {
+ "name": "member_user_org_unique_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "member_user_id_user_id_fk": {
+ "name": "member_user_id_user_id_fk",
+ "tableFrom": "member",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "member_organization_id_organization_id_fk": {
+ "name": "member_organization_id_organization_id_fk",
+ "tableFrom": "member",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization": {
+ "name": "organization",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_slug_unique": {
+ "name": "organization_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.rate_limit": {
+ "name": "rate_limit",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "count": {
+ "name": "count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_request": {
+ "name": "last_request",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "rate_limit_key_idx": {
+ "name": "rate_limit_key_idx",
+ "columns": [
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "session_user_id_idx": {
+ "name": "session_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.subscription": {
+ "name": "subscription",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "plan": {
+ "name": "plan",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reference_id": {
+ "name": "reference_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_subscription_id": {
+ "name": "stripe_subscription_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'incomplete'"
+ },
+ "period_start": {
+ "name": "period_start",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "period_end": {
+ "name": "period_end",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trial_start": {
+ "name": "trial_start",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trial_end": {
+ "name": "trial_end",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancel_at_period_end": {
+ "name": "cancel_at_period_end",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "cancel_at": {
+ "name": "cancel_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "canceled_at": {
+ "name": "canceled_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ended_at": {
+ "name": "ended_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "seats": {
+ "name": "seats",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "billing_interval": {
+ "name": "billing_interval",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_schedule_id": {
+ "name": "stripe_schedule_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "subscription_reference_id_idx": {
+ "name": "subscription_reference_id_idx",
+ "columns": [
+ {
+ "expression": "reference_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "subscription_stripe_customer_id_idx": {
+ "name": "subscription_stripe_customer_id_idx",
+ "columns": [
+ {
+ "expression": "stripe_customer_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "verification_identifier_idx": {
+ "name": "verification_identifier_idx",
+ "columns": [
+ {
+ "expression": "identifier",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.activity_log": {
+ "name": "activity_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "activity_action",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_type": {
+ "name": "resource_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_id": {
+ "name": "resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "activity_log_organization_id_idx": {
+ "name": "activity_log_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_actor_id_idx": {
+ "name": "activity_log_actor_id_idx",
+ "columns": [
+ {
+ "expression": "actor_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_resource_idx": {
+ "name": "activity_log_resource_idx",
+ "columns": [
+ {
+ "expression": "resource_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "resource_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_created_at_idx": {
+ "name": "activity_log_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "activity_log_organization_id_organization_id_fk": {
+ "name": "activity_log_organization_id_organization_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "activity_log_actor_id_user_id_fk": {
+ "name": "activity_log_actor_id_user_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "user",
+ "columnsFrom": [
+ "actor_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ai_config": {
+ "name": "ai_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Default'"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openai'"
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'gpt-4o-mini'"
+ },
+ "api_key_encrypted": {
+ "name": "api_key_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 4096
+ },
+ "input_price_per_1m": {
+ "name": "input_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output_price_per_1m": {
+ "name": "output_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default_chatbot": {
+ "name": "is_default_chatbot",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_default_analysis": {
+ "name": "is_default_analysis",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ai_config_organization_id_idx": {
+ "name": "ai_config_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ai_config_default_chatbot_idx": {
+ "name": "ai_config_default_chatbot_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"ai_config\".\"is_default_chatbot\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ai_config_default_analysis_idx": {
+ "name": "ai_config_default_analysis_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"ai_config\".\"is_default_analysis\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ai_config_organization_id_organization_id_fk": {
+ "name": "ai_config_organization_id_organization_id_fk",
+ "tableFrom": "ai_config",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.analysis_run": {
+ "name": "analysis_run",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "analysis_run_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'completed'"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "criteria_snapshot": {
+ "name": "criteria_snapshot",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "composite_score": {
+ "name": "composite_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "prompt_tokens": {
+ "name": "prompt_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completion_tokens": {
+ "name": "completion_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cost_usd_micros": {
+ "name": "cost_usd_micros",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "billing_mode": {
+ "name": "billing_mode",
+ "type": "analysis_billing_mode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'byok'"
+ },
+ "raw_response": {
+ "name": "raw_response",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scored_by_id": {
+ "name": "scored_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "analysis_run_organization_id_idx": {
+ "name": "analysis_run_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "analysis_run_application_id_idx": {
+ "name": "analysis_run_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "analysis_run_created_at_idx": {
+ "name": "analysis_run_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "analysis_run_organization_id_organization_id_fk": {
+ "name": "analysis_run_organization_id_organization_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "analysis_run_application_id_application_id_fk": {
+ "name": "analysis_run_application_id_application_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "analysis_run_scored_by_id_user_id_fk": {
+ "name": "analysis_run_scored_by_id_user_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "user",
+ "columnsFrom": [
+ "scored_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.application": {
+ "name": "application",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "application_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'new'"
+ },
+ "score": {
+ "name": "score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cover_letter_text": {
+ "name": "cover_letter_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "application_organization_id_idx": {
+ "name": "application_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_candidate_id_idx": {
+ "name": "application_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_job_id_idx": {
+ "name": "application_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_org_candidate_job_idx": {
+ "name": "application_org_candidate_job_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "application_organization_id_organization_id_fk": {
+ "name": "application_organization_id_organization_id_fk",
+ "tableFrom": "application",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_candidate_id_candidate_id_fk": {
+ "name": "application_candidate_id_candidate_id_fk",
+ "tableFrom": "application",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_job_id_job_id_fk": {
+ "name": "application_job_id_job_id_fk",
+ "tableFrom": "application",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.application_source": {
+ "name": "application_source",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "source_channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'direct'"
+ },
+ "tracking_link_id": {
+ "name": "tracking_link_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_source": {
+ "name": "utm_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_medium": {
+ "name": "utm_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_campaign": {
+ "name": "utm_campaign",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_term": {
+ "name": "utm_term",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_content": {
+ "name": "utm_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "referrer_domain": {
+ "name": "referrer_domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "application_source_organization_id_idx": {
+ "name": "application_source_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_application_id_idx": {
+ "name": "application_source_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_channel_idx": {
+ "name": "application_source_channel_idx",
+ "columns": [
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_tracking_link_id_idx": {
+ "name": "application_source_tracking_link_id_idx",
+ "columns": [
+ {
+ "expression": "tracking_link_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_application_idx": {
+ "name": "application_source_application_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "application_source_organization_id_organization_id_fk": {
+ "name": "application_source_organization_id_organization_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_source_application_id_application_id_fk": {
+ "name": "application_source_application_id_application_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_source_tracking_link_id_tracking_link_id_fk": {
+ "name": "application_source_tracking_link_id_tracking_link_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "tracking_link",
+ "columnsFrom": [
+ "tracking_link_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.calendar_integration": {
+ "name": "calendar_integration",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "calendar_provider",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'google'"
+ },
+ "access_token_encrypted": {
+ "name": "access_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token_encrypted": {
+ "name": "refresh_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "calendar_id": {
+ "name": "calendar_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'primary'"
+ },
+ "account_email": {
+ "name": "account_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_channel_id": {
+ "name": "webhook_channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_resource_id": {
+ "name": "webhook_resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_expiration": {
+ "name": "webhook_expiration",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_token": {
+ "name": "sync_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "calendar_integration_user_provider_idx": {
+ "name": "calendar_integration_user_provider_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "calendar_integration_webhook_channel_idx": {
+ "name": "calendar_integration_webhook_channel_idx",
+ "columns": [
+ {
+ "expression": "webhook_channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "calendar_integration_user_id_user_id_fk": {
+ "name": "calendar_integration_user_id_user_id_fk",
+ "tableFrom": "calendar_integration",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate": {
+ "name": "candidate",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone": {
+ "name": "phone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gender": {
+ "name": "gender",
+ "type": "gender",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "date_of_birth": {
+ "name": "date_of_birth",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "quick_notes": {
+ "name": "quick_notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "retention_exempt_until": {
+ "name": "retention_exempt_until",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "retention_exempt_reason": {
+ "name": "retention_exempt_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "retention_reviewed_at": {
+ "name": "retention_reviewed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "quarantined_at": {
+ "name": "quarantined_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scheduled_purge_at": {
+ "name": "scheduled_purge_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_organization_id_idx": {
+ "name": "candidate_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_gender_idx": {
+ "name": "candidate_gender_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "gender",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_org_email_idx": {
+ "name": "candidate_org_email_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_quarantine_idx": {
+ "name": "candidate_quarantine_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scheduled_purge_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_organization_id_organization_id_fk": {
+ "name": "candidate_organization_id_organization_id_fk",
+ "tableFrom": "candidate",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_conversation": {
+ "name": "candidate_conversation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reply_token": {
+ "name": "reply_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "unread_count": {
+ "name": "unread_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_message_at": {
+ "name": "last_message_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_conversation_organization_id_idx": {
+ "name": "candidate_conversation_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_conversation_application_id_idx": {
+ "name": "candidate_conversation_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_conversation_reply_token_idx": {
+ "name": "candidate_conversation_reply_token_idx",
+ "columns": [
+ {
+ "expression": "reply_token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_conversation_last_message_at_idx": {
+ "name": "candidate_conversation_last_message_at_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "last_message_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_conversation_organization_id_organization_id_fk": {
+ "name": "candidate_conversation_organization_id_organization_id_fk",
+ "tableFrom": "candidate_conversation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_conversation_application_id_application_id_fk": {
+ "name": "candidate_conversation_application_id_application_id_fk",
+ "tableFrom": "candidate_conversation",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_message": {
+ "name": "candidate_message",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "conversation_id": {
+ "name": "conversation_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "direction": {
+ "name": "direction",
+ "type": "candidate_message_direction",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "candidate_message_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "from_email": {
+ "name": "from_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "to_email": {
+ "name": "to_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_text": {
+ "name": "body_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'message'"
+ },
+ "interview_id": {
+ "name": "interview_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "calendar_attachment_status": {
+ "name": "calendar_attachment_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'not_applicable'"
+ },
+ "calendar_attachment_error": {
+ "name": "calendar_attachment_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "calendar_sequence": {
+ "name": "calendar_sequence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "internet_message_id": {
+ "name": "internet_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "in_reply_to": {
+ "name": "in_reply_to",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "references": {
+ "name": "references",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_by_id": {
+ "name": "sent_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_status_at": {
+ "name": "provider_status_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_at": {
+ "name": "sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "delivered_at": {
+ "name": "delivered_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failed_at": {
+ "name": "failed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_code": {
+ "name": "error_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_message_organization_id_idx": {
+ "name": "candidate_message_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_conversation_id_idx": {
+ "name": "candidate_message_conversation_id_idx",
+ "columns": [
+ {
+ "expression": "conversation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_interview_id_idx": {
+ "name": "candidate_message_interview_id_idx",
+ "columns": [
+ {
+ "expression": "interview_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_provider_id_idx": {
+ "name": "candidate_message_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_internet_id_idx": {
+ "name": "candidate_message_internet_id_idx",
+ "columns": [
+ {
+ "expression": "internet_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_message_organization_id_organization_id_fk": {
+ "name": "candidate_message_organization_id_organization_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_message_conversation_id_candidate_conversation_id_fk": {
+ "name": "candidate_message_conversation_id_candidate_conversation_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "candidate_conversation",
+ "columnsFrom": [
+ "conversation_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_message_interview_id_interview_id_fk": {
+ "name": "candidate_message_interview_id_interview_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "interview",
+ "columnsFrom": [
+ "interview_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "candidate_message_sent_by_id_user_id_fk": {
+ "name": "candidate_message_sent_by_id_user_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "user",
+ "columnsFrom": [
+ "sent_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_message_attachment": {
+ "name": "candidate_message_attachment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message_id": {
+ "name": "message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "storage_key": {
+ "name": "storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_attachment_id": {
+ "name": "provider_attachment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_message_attachment_organization_id_idx": {
+ "name": "candidate_message_attachment_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_attachment_message_id_idx": {
+ "name": "candidate_message_attachment_message_id_idx",
+ "columns": [
+ {
+ "expression": "message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_message_attachment_organization_id_organization_id_fk": {
+ "name": "candidate_message_attachment_organization_id_organization_id_fk",
+ "tableFrom": "candidate_message_attachment",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_message_attachment_message_id_candidate_message_id_fk": {
+ "name": "candidate_message_attachment_message_id_candidate_message_id_fk",
+ "tableFrom": "candidate_message_attachment",
+ "tableTo": "candidate_message",
+ "columnsFrom": [
+ "message_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "candidate_message_attachment_storage_key_unique": {
+ "name": "candidate_message_attachment_storage_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "storage_key"
+ ]
+ },
+ "candidate_message_attachment_provider_attachment_id_unique": {
+ "name": "candidate_message_attachment_provider_attachment_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_attachment_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_message_webhook_event": {
+ "name": "candidate_message_webhook_event",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "occurred_at": {
+ "name": "occurred_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "processed_at": {
+ "name": "processed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_message_webhook_provider_id_idx": {
+ "name": "candidate_message_webhook_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_webhook_unprocessed_idx": {
+ "name": "candidate_message_webhook_unprocessed_idx",
+ "columns": [
+ {
+ "expression": "processed_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.career_page": {
+ "name": "career_page",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "accent_color": {
+ "name": "accent_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "headline": {
+ "name": "headline",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "logo_storage_key": {
+ "name": "logo_storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "banner_storage_key": {
+ "name": "banner_storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "banner_position": {
+ "name": "banner_position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "career_page_organization_id_idx": {
+ "name": "career_page_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "career_page_slug_idx": {
+ "name": "career_page_slug_idx",
+ "columns": [
+ {
+ "expression": "slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "career_page_organization_id_organization_id_fk": {
+ "name": "career_page_organization_id_organization_id_fk",
+ "tableFrom": "career_page",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_agent": {
+ "name": "chatbot_agent",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "system_prompt": {
+ "name": "system_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "temperature": {
+ "name": "temperature",
+ "type": "numeric(3, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_agent_org_user_idx": {
+ "name": "chatbot_agent_org_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "chatbot_agent_default_per_user_idx": {
+ "name": "chatbot_agent_default_per_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"chatbot_agent\".\"is_default\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_agent_organization_id_organization_id_fk": {
+ "name": "chatbot_agent_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_agent",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_agent_user_id_user_id_fk": {
+ "name": "chatbot_agent_user_id_user_id_fk",
+ "tableFrom": "chatbot_agent",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_conversation": {
+ "name": "chatbot_conversation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "folder_id": {
+ "name": "folder_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ai_config_id": {
+ "name": "ai_config_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'New chat'"
+ },
+ "scope": {
+ "name": "scope",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thinking": {
+ "name": "thinking",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "pinned": {
+ "name": "pinned",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "last_message_preview": {
+ "name": "last_message_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message_at": {
+ "name": "last_message_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_conversation_org_user_idx": {
+ "name": "chatbot_conversation_org_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "chatbot_conversation_folder_idx": {
+ "name": "chatbot_conversation_folder_idx",
+ "columns": [
+ {
+ "expression": "folder_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "chatbot_conversation_last_message_at_idx": {
+ "name": "chatbot_conversation_last_message_at_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "last_message_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_conversation_organization_id_organization_id_fk": {
+ "name": "chatbot_conversation_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_user_id_user_id_fk": {
+ "name": "chatbot_conversation_user_id_user_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_folder_id_chatbot_folder_id_fk": {
+ "name": "chatbot_conversation_folder_id_chatbot_folder_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "chatbot_folder",
+ "columnsFrom": [
+ "folder_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_agent_id_chatbot_agent_id_fk": {
+ "name": "chatbot_conversation_agent_id_chatbot_agent_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "chatbot_agent",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_ai_config_id_ai_config_id_fk": {
+ "name": "chatbot_conversation_ai_config_id_ai_config_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "ai_config",
+ "columnsFrom": [
+ "ai_config_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_folder": {
+ "name": "chatbot_folder",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "position": {
+ "name": "position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_folder_org_user_idx": {
+ "name": "chatbot_folder_org_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_folder_organization_id_organization_id_fk": {
+ "name": "chatbot_folder_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_folder",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_folder_user_id_user_id_fk": {
+ "name": "chatbot_folder_user_id_user_id_fk",
+ "tableFrom": "chatbot_folder",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_message": {
+ "name": "chatbot_message",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "conversation_id": {
+ "name": "conversation_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "chatbot_message_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "reasoning": {
+ "name": "reasoning",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tool_calls": {
+ "name": "tool_calls",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sources": {
+ "name": "sources",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attachments": {
+ "name": "attachments",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_message_conversation_idx": {
+ "name": "chatbot_message_conversation_idx",
+ "columns": [
+ {
+ "expression": "conversation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_message_conversation_id_chatbot_conversation_id_fk": {
+ "name": "chatbot_message_conversation_id_chatbot_conversation_id_fk",
+ "tableFrom": "chatbot_message",
+ "tableTo": "chatbot_conversation",
+ "columnsFrom": [
+ "conversation_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_message_organization_id_organization_id_fk": {
+ "name": "chatbot_message_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_message",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_message_user_id_user_id_fk": {
+ "name": "chatbot_message_user_id_user_id_fk",
+ "tableFrom": "chatbot_message",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.comment": {
+ "name": "comment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_id": {
+ "name": "author_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "comment_target",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "comment_organization_id_idx": {
+ "name": "comment_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_target_idx": {
+ "name": "comment_target_idx",
+ "columns": [
+ {
+ "expression": "target_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_author_id_idx": {
+ "name": "comment_author_id_idx",
+ "columns": [
+ {
+ "expression": "author_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "comment_organization_id_organization_id_fk": {
+ "name": "comment_organization_id_organization_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_author_id_user_id_fk": {
+ "name": "comment_author_id_user_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "user",
+ "columnsFrom": [
+ "author_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.criterion_score": {
+ "name": "criterion_score",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "criterion_key": {
+ "name": "criterion_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "max_score": {
+ "name": "max_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "applicant_score": {
+ "name": "applicant_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "confidence": {
+ "name": "confidence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "evidence": {
+ "name": "evidence",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "strengths": {
+ "name": "strengths",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gaps": {
+ "name": "gaps",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "criterion_score_organization_id_idx": {
+ "name": "criterion_score_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "criterion_score_application_id_idx": {
+ "name": "criterion_score_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "criterion_score_app_criterion_idx": {
+ "name": "criterion_score_app_criterion_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "criterion_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "criterion_score_organization_id_organization_id_fk": {
+ "name": "criterion_score_organization_id_organization_id_fk",
+ "tableFrom": "criterion_score",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "criterion_score_application_id_application_id_fk": {
+ "name": "criterion_score_application_id_application_id_fk",
+ "tableFrom": "criterion_score",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.document": {
+ "name": "document",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "document_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'resume'"
+ },
+ "storage_key": {
+ "name": "storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "original_filename": {
+ "name": "original_filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "parsed_content": {
+ "name": "parsed_content",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "document_organization_id_idx": {
+ "name": "document_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "document_candidate_id_idx": {
+ "name": "document_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "document_organization_id_organization_id_fk": {
+ "name": "document_organization_id_organization_id_fk",
+ "tableFrom": "document",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "document_candidate_id_candidate_id_fk": {
+ "name": "document_candidate_id_candidate_id_fk",
+ "tableFrom": "document",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "document_storage_key_unique": {
+ "name": "document_storage_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "storage_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_suppression": {
+ "name": "email_suppression",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "email_suppression_reason",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_suppression_email_idx": {
+ "name": "email_suppression_email_idx",
+ "columns": [
+ {
+ "expression": "lower(\"email\")",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_template": {
+ "name": "email_template",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_template_organization_id_idx": {
+ "name": "email_template_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "email_template_created_by_id_idx": {
+ "name": "email_template_created_by_id_idx",
+ "columns": [
+ {
+ "expression": "created_by_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_template_organization_id_organization_id_fk": {
+ "name": "email_template_organization_id_organization_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "email_template_created_by_id_user_id_fk": {
+ "name": "email_template_created_by_id_user_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.import_job": {
+ "name": "import_job",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "import_source",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "import_job_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'processing'"
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mapping": {
+ "name": "mapping",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "target_job_id": {
+ "name": "target_job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_rows": {
+ "name": "total_rows",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "committed_rows": {
+ "name": "committed_rows",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "import_job_organization_id_idx": {
+ "name": "import_job_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "import_job_status_idx": {
+ "name": "import_job_status_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "import_job_organization_id_organization_id_fk": {
+ "name": "import_job_organization_id_organization_id_fk",
+ "tableFrom": "import_job",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_job_created_by_user_id_fk": {
+ "name": "import_job_created_by_user_id_fk",
+ "tableFrom": "import_job",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_job_target_job_id_job_id_fk": {
+ "name": "import_job_target_job_id_job_id_fk",
+ "tableFrom": "import_job",
+ "tableTo": "job",
+ "columnsFrom": [
+ "target_job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.import_row": {
+ "name": "import_row",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "row_index": {
+ "name": "row_index",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "raw_data": {
+ "name": "raw_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_data": {
+ "name": "normalized_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "import_row_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'ready'"
+ },
+ "dedupe_hash": {
+ "name": "dedupe_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "matched_candidate_id": {
+ "name": "matched_candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_candidate_id": {
+ "name": "created_candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "import_row_job_id_idx": {
+ "name": "import_row_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "import_row_job_status_idx": {
+ "name": "import_row_job_status_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "import_row_dedupe_idx": {
+ "name": "import_row_dedupe_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "dedupe_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "import_row_organization_id_organization_id_fk": {
+ "name": "import_row_organization_id_organization_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_row_job_id_import_job_id_fk": {
+ "name": "import_row_job_id_import_job_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "import_job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_row_matched_candidate_id_candidate_id_fk": {
+ "name": "import_row_matched_candidate_id_candidate_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "matched_candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "import_row_created_candidate_id_candidate_id_fk": {
+ "name": "import_row_created_candidate_id_candidate_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "created_candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.interview": {
+ "name": "interview",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "interview_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'video'"
+ },
+ "status": {
+ "name": "status",
+ "type": "interview_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'scheduled'"
+ },
+ "scheduled_at": {
+ "name": "scheduled_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "duration": {
+ "name": "duration",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 60
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "personal_note": {
+ "name": "personal_note",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "interviewers": {
+ "name": "interviewers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "invitation_sent_at": {
+ "name": "invitation_sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "candidate_response": {
+ "name": "candidate_response",
+ "type": "candidate_response",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "candidate_responded_at": {
+ "name": "candidate_responded_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_calendar_event_id": {
+ "name": "google_calendar_event_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_calendar_event_link": {
+ "name": "google_calendar_event_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'UTC'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "interview_organization_id_idx": {
+ "name": "interview_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_application_id_idx": {
+ "name": "interview_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_scheduled_at_idx": {
+ "name": "interview_scheduled_at_idx",
+ "columns": [
+ {
+ "expression": "scheduled_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_status_idx": {
+ "name": "interview_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_created_by_id_idx": {
+ "name": "interview_created_by_id_idx",
+ "columns": [
+ {
+ "expression": "created_by_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "interview_organization_id_organization_id_fk": {
+ "name": "interview_organization_id_organization_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "interview_application_id_application_id_fk": {
+ "name": "interview_application_id_application_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "interview_created_by_id_user_id_fk": {
+ "name": "interview_created_by_id_user_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invite_link": {
+ "name": "invite_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'member'"
+ },
+ "max_uses": {
+ "name": "max_uses",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "use_count": {
+ "name": "use_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "invite_link_organization_id_idx": {
+ "name": "invite_link_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invite_link_token_idx": {
+ "name": "invite_link_token_idx",
+ "columns": [
+ {
+ "expression": "token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invite_link_organization_id_organization_id_fk": {
+ "name": "invite_link_organization_id_organization_id_fk",
+ "tableFrom": "invite_link",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invite_link_created_by_id_user_id_fk": {
+ "name": "invite_link_created_by_id_user_id_fk",
+ "tableFrom": "invite_link",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "invite_link_token_unique": {
+ "name": "invite_link_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job": {
+ "name": "job",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "job_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'full_time'"
+ },
+ "status": {
+ "name": "status",
+ "type": "job_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "salary_min": {
+ "name": "salary_min",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_max": {
+ "name": "salary_max",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_currency": {
+ "name": "salary_currency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_unit": {
+ "name": "salary_unit",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_negotiable": {
+ "name": "salary_negotiable",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "remote_status": {
+ "name": "remote_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "valid_through": {
+ "name": "valid_through",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "experience_level": {
+ "name": "experience_level",
+ "type": "experience_level",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "phone_requirement": {
+ "name": "phone_requirement",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'optional'"
+ },
+ "require_resume": {
+ "name": "require_resume",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "require_cover_letter": {
+ "name": "require_cover_letter",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "auto_score_on_apply": {
+ "name": "auto_score_on_apply",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "analysis_context": {
+ "name": "analysis_context",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{\"coverLetter\":true,\"screeningAnswers\":true,\"recruiterNotes\":false}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "job_organization_id_idx": {
+ "name": "job_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_organization_id_organization_id_fk": {
+ "name": "job_organization_id_organization_id_fk",
+ "tableFrom": "job",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "job_slug_unique": {
+ "name": "job_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job_question": {
+ "name": "job_question",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "question_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'short_text'"
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "required": {
+ "name": "required",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "options": {
+ "name": "options",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "job_question_organization_id_idx": {
+ "name": "job_question_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "job_question_job_id_idx": {
+ "name": "job_question_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_question_organization_id_organization_id_fk": {
+ "name": "job_question_organization_id_organization_id_fk",
+ "tableFrom": "job_question",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "job_question_job_id_job_id_fk": {
+ "name": "job_question_job_id_job_id_fk",
+ "tableFrom": "job_question",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.join_request": {
+ "name": "join_request",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "join_request_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "reviewed_by_id": {
+ "name": "reviewed_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reviewed_at": {
+ "name": "reviewed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "join_request_organization_id_idx": {
+ "name": "join_request_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "join_request_user_id_idx": {
+ "name": "join_request_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "join_request_status_idx": {
+ "name": "join_request_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "join_request_user_id_user_id_fk": {
+ "name": "join_request_user_id_user_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "join_request_organization_id_organization_id_fk": {
+ "name": "join_request_organization_id_organization_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "join_request_reviewed_by_id_user_id_fk": {
+ "name": "join_request_reviewed_by_id_user_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "user",
+ "columnsFrom": [
+ "reviewed_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification_outbox": {
+ "name": "notification_outbox",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipient_user_id": {
+ "name": "recipient_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipient_email": {
+ "name": "recipient_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "notification_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cadence": {
+ "name": "cadence",
+ "type": "notification_cadence",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "notification_outbox_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempts": {
+ "name": "attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_attempt_at": {
+ "name": "next_attempt_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "digest_bucket": {
+ "name": "digest_bucket",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_at": {
+ "name": "sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failed_at": {
+ "name": "failed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "notification_outbox_dedupe_key_idx": {
+ "name": "notification_outbox_dedupe_key_idx",
+ "columns": [
+ {
+ "expression": "dedupe_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_pull_idx": {
+ "name": "notification_outbox_pull_idx",
+ "columns": [
+ {
+ "expression": "cadence",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_attempt_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"notification_outbox\".\"status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_provider_id_idx": {
+ "name": "notification_outbox_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"notification_outbox\".\"provider_message_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_organization_id_idx": {
+ "name": "notification_outbox_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_digest_idx": {
+ "name": "notification_outbox_digest_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "recipient_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "digest_bucket",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "notification_outbox_organization_id_organization_id_fk": {
+ "name": "notification_outbox_organization_id_organization_id_fk",
+ "tableFrom": "notification_outbox",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_outbox_recipient_user_id_user_id_fk": {
+ "name": "notification_outbox_recipient_user_id_user_id_fk",
+ "tableFrom": "notification_outbox",
+ "tableTo": "user",
+ "columnsFrom": [
+ "recipient_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification_preference": {
+ "name": "notification_preference",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "notification_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_mode": {
+ "name": "channel_mode",
+ "type": "notification_channel_mode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "notification_preference_user_org_type_idx": {
+ "name": "notification_preference_user_org_type_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_preference_organization_id_idx": {
+ "name": "notification_preference_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "notification_preference_user_id_user_id_fk": {
+ "name": "notification_preference_user_id_user_id_fk",
+ "tableFrom": "notification_preference",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_preference_organization_id_organization_id_fk": {
+ "name": "notification_preference_organization_id_organization_id_fk",
+ "tableFrom": "notification_preference",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.onboarding_survey_response": {
+ "name": "onboarding_survey_response",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "signup_plan": {
+ "name": "signup_plan",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "signup_billing": {
+ "name": "signup_billing",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "company_size": {
+ "name": "company_size",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_role": {
+ "name": "user_role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discovery_source": {
+ "name": "discovery_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "current_hiring_process": {
+ "name": "current_hiring_process",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expected_roles_12m": {
+ "name": "expected_roles_12m",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "answered_count": {
+ "name": "answered_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "skipped_count": {
+ "name": "skipped_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "onboarding_survey_response_user_id_idx": {
+ "name": "onboarding_survey_response_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "onboarding_survey_response_organization_id_idx": {
+ "name": "onboarding_survey_response_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "onboarding_survey_response_user_id_user_id_fk": {
+ "name": "onboarding_survey_response_user_id_user_id_fk",
+ "tableFrom": "onboarding_survey_response",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "onboarding_survey_response_organization_id_organization_id_fk": {
+ "name": "onboarding_survey_response_organization_id_organization_id_fk",
+ "tableFrom": "onboarding_survey_response",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.org_settings": {
+ "name": "org_settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name_display_format": {
+ "name": "name_display_format",
+ "type": "name_display_format",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'first_last'"
+ },
+ "date_format": {
+ "name": "date_format",
+ "type": "date_format",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'mdy'"
+ },
+ "retention_enabled": {
+ "name": "retention_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "retention_months": {
+ "name": "retention_months",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 24
+ },
+ "quarantine_days": {
+ "name": "quarantine_days",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 30
+ },
+ "retention_activated_at": {
+ "name": "retention_activated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "privacy_policy_url": {
+ "name": "privacy_policy_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "privacy_policy_text": {
+ "name": "privacy_policy_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "privacy_contact_email": {
+ "name": "privacy_contact_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "org_settings_organization_id_idx": {
+ "name": "org_settings_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "org_settings_organization_id_organization_id_fk": {
+ "name": "org_settings_organization_id_organization_id_fk",
+ "tableFrom": "org_settings",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.platform_ai_config": {
+ "name": "platform_ai_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Platform (OpenRouter)'"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openrouter'"
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openai/gpt-5.4-mini'"
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 4096
+ },
+ "input_price_per_1m": {
+ "name": "input_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output_price_per_1m": {
+ "name": "output_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default_analysis": {
+ "name": "is_default_analysis",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "is_enabled": {
+ "name": "is_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "platform_ai_config_organization_id_idx": {
+ "name": "platform_ai_config_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "platform_ai_config_organization_id_organization_id_fk": {
+ "name": "platform_ai_config_organization_id_organization_id_fk",
+ "tableFrom": "platform_ai_config",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.property_definition": {
+ "name": "property_definition",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "property_entity_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "property_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "property_definition_org_idx": {
+ "name": "property_definition_org_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_definition_org_entity_idx": {
+ "name": "property_definition_org_entity_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_definition_job_idx": {
+ "name": "property_definition_job_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "property_definition_organization_id_organization_id_fk": {
+ "name": "property_definition_organization_id_organization_id_fk",
+ "tableFrom": "property_definition",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "property_definition_job_id_job_id_fk": {
+ "name": "property_definition_job_id_job_id_fk",
+ "tableFrom": "property_definition",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.property_value": {
+ "name": "property_value",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "property_definition_id": {
+ "name": "property_definition_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "property_entity_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entity_id": {
+ "name": "entity_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "property_value_org_idx": {
+ "name": "property_value_org_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_value_entity_idx": {
+ "name": "property_value_entity_idx",
+ "columns": [
+ {
+ "expression": "entity_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_value_definition_idx": {
+ "name": "property_value_definition_idx",
+ "columns": [
+ {
+ "expression": "property_definition_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_value_def_entity_idx": {
+ "name": "property_value_def_entity_idx",
+ "columns": [
+ {
+ "expression": "property_definition_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "property_value_organization_id_organization_id_fk": {
+ "name": "property_value_organization_id_organization_id_fk",
+ "tableFrom": "property_value",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "property_value_property_definition_id_property_definition_id_fk": {
+ "name": "property_value_property_definition_id_property_definition_id_fk",
+ "tableFrom": "property_value",
+ "tableTo": "property_definition",
+ "columnsFrom": [
+ "property_definition_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.question_response": {
+ "name": "question_response",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "question_id": {
+ "name": "question_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "question_response_organization_id_idx": {
+ "name": "question_response_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "question_response_application_id_idx": {
+ "name": "question_response_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "question_response_question_id_idx": {
+ "name": "question_response_question_id_idx",
+ "columns": [
+ {
+ "expression": "question_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "question_response_organization_id_organization_id_fk": {
+ "name": "question_response_organization_id_organization_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "question_response_application_id_application_id_fk": {
+ "name": "question_response_application_id_application_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "question_response_question_id_job_question_id_fk": {
+ "name": "question_response_question_id_job_question_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "job_question",
+ "columnsFrom": [
+ "question_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.retention_audit": {
+ "name": "retention_audit",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "retention_audit_action",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "result": {
+ "name": "result",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'success'"
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "retention_audit_organization_id_idx": {
+ "name": "retention_audit_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "retention_audit_candidate_id_idx": {
+ "name": "retention_audit_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "retention_audit_created_at_idx": {
+ "name": "retention_audit_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "retention_audit_organization_id_organization_id_fk": {
+ "name": "retention_audit_organization_id_organization_id_fk",
+ "tableFrom": "retention_audit",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scoring_criterion": {
+ "name": "scoring_criterion",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "category": {
+ "name": "category",
+ "type": "criterion_category",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'custom'"
+ },
+ "max_score": {
+ "name": "max_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 10
+ },
+ "weight": {
+ "name": "weight",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scoring_criterion_organization_id_idx": {
+ "name": "scoring_criterion_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scoring_criterion_job_id_idx": {
+ "name": "scoring_criterion_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scoring_criterion_job_key_idx": {
+ "name": "scoring_criterion_job_key_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scoring_criterion_organization_id_organization_id_fk": {
+ "name": "scoring_criterion_organization_id_organization_id_fk",
+ "tableFrom": "scoring_criterion",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "scoring_criterion_job_id_job_id_fk": {
+ "name": "scoring_criterion_job_id_job_id_fk",
+ "tableFrom": "scoring_criterion",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tracking_link": {
+ "name": "tracking_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "channel": {
+ "name": "channel",
+ "type": "source_channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'custom'"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code": {
+ "name": "code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "utm_source": {
+ "name": "utm_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_medium": {
+ "name": "utm_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_campaign": {
+ "name": "utm_campaign",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_term": {
+ "name": "utm_term",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_content": {
+ "name": "utm_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "click_count": {
+ "name": "click_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "application_count": {
+ "name": "application_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "tracking_link_organization_id_idx": {
+ "name": "tracking_link_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_job_id_idx": {
+ "name": "tracking_link_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_code_idx": {
+ "name": "tracking_link_code_idx",
+ "columns": [
+ {
+ "expression": "code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_channel_idx": {
+ "name": "tracking_link_channel_idx",
+ "columns": [
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tracking_link_organization_id_organization_id_fk": {
+ "name": "tracking_link_organization_id_organization_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tracking_link_job_id_job_id_fk": {
+ "name": "tracking_link_job_id_job_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tracking_link_created_by_id_user_id_fk": {
+ "name": "tracking_link_created_by_id_user_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "tracking_link_code_unique": {
+ "name": "tracking_link_code_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "code"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sso_provider": {
+ "name": "sso_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oidc_config": {
+ "name": "oidc_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "saml_config": {
+ "name": "saml_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "sso_provider_domain_idx": {
+ "name": "sso_provider_domain_idx",
+ "columns": [
+ {
+ "expression": "domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "sso_provider_provider_id_idx": {
+ "name": "sso_provider_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "sso_provider_organization_id_idx": {
+ "name": "sso_provider_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "sso_provider_user_id_user_id_fk": {
+ "name": "sso_provider_user_id_user_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "sso_provider_organization_id_organization_id_fk": {
+ "name": "sso_provider_organization_id_organization_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.activity_action": {
+ "name": "activity_action",
+ "schema": "public",
+ "values": [
+ "created",
+ "updated",
+ "deleted",
+ "status_changed",
+ "comment_added",
+ "member_invited",
+ "member_removed",
+ "member_role_changed",
+ "scored"
+ ]
+ },
+ "public.analysis_billing_mode": {
+ "name": "analysis_billing_mode",
+ "schema": "public",
+ "values": [
+ "platform",
+ "byok"
+ ]
+ },
+ "public.analysis_run_status": {
+ "name": "analysis_run_status",
+ "schema": "public",
+ "values": [
+ "completed",
+ "failed",
+ "partial"
+ ]
+ },
+ "public.application_status": {
+ "name": "application_status",
+ "schema": "public",
+ "values": [
+ "new",
+ "screening",
+ "interview",
+ "offer",
+ "hired",
+ "rejected"
+ ]
+ },
+ "public.calendar_provider": {
+ "name": "calendar_provider",
+ "schema": "public",
+ "values": [
+ "google"
+ ]
+ },
+ "public.candidate_message_direction": {
+ "name": "candidate_message_direction",
+ "schema": "public",
+ "values": [
+ "inbound",
+ "outbound"
+ ]
+ },
+ "public.candidate_message_status": {
+ "name": "candidate_message_status",
+ "schema": "public",
+ "values": [
+ "queued",
+ "sent",
+ "delivered",
+ "delayed",
+ "bounced",
+ "failed",
+ "complained"
+ ]
+ },
+ "public.candidate_response": {
+ "name": "candidate_response",
+ "schema": "public",
+ "values": [
+ "pending",
+ "accepted",
+ "declined",
+ "tentative"
+ ]
+ },
+ "public.chatbot_message_role": {
+ "name": "chatbot_message_role",
+ "schema": "public",
+ "values": [
+ "user",
+ "assistant"
+ ]
+ },
+ "public.comment_target": {
+ "name": "comment_target",
+ "schema": "public",
+ "values": [
+ "candidate",
+ "application",
+ "job"
+ ]
+ },
+ "public.criterion_category": {
+ "name": "criterion_category",
+ "schema": "public",
+ "values": [
+ "technical",
+ "experience",
+ "soft_skills",
+ "education",
+ "culture",
+ "custom"
+ ]
+ },
+ "public.date_format": {
+ "name": "date_format",
+ "schema": "public",
+ "values": [
+ "mdy",
+ "dmy",
+ "ymd"
+ ]
+ },
+ "public.document_type": {
+ "name": "document_type",
+ "schema": "public",
+ "values": [
+ "resume",
+ "cover_letter",
+ "other"
+ ]
+ },
+ "public.email_suppression_reason": {
+ "name": "email_suppression_reason",
+ "schema": "public",
+ "values": [
+ "bounce",
+ "complaint"
+ ]
+ },
+ "public.experience_level": {
+ "name": "experience_level",
+ "schema": "public",
+ "values": [
+ "junior",
+ "mid",
+ "senior",
+ "lead"
+ ]
+ },
+ "public.gender": {
+ "name": "gender",
+ "schema": "public",
+ "values": [
+ "male",
+ "female",
+ "other",
+ "prefer_not_to_say"
+ ]
+ },
+ "public.import_job_status": {
+ "name": "import_job_status",
+ "schema": "public",
+ "values": [
+ "processing",
+ "previewing",
+ "committing",
+ "completed",
+ "failed"
+ ]
+ },
+ "public.import_row_status": {
+ "name": "import_row_status",
+ "schema": "public",
+ "values": [
+ "ready",
+ "duplicate",
+ "duplicate_in_file",
+ "error",
+ "committed",
+ "skipped"
+ ]
+ },
+ "public.import_source": {
+ "name": "import_source",
+ "schema": "public",
+ "values": [
+ "csv",
+ "xlsx",
+ "resume_zip"
+ ]
+ },
+ "public.interview_status": {
+ "name": "interview_status",
+ "schema": "public",
+ "values": [
+ "scheduled",
+ "completed",
+ "cancelled",
+ "no_show"
+ ]
+ },
+ "public.interview_type": {
+ "name": "interview_type",
+ "schema": "public",
+ "values": [
+ "phone",
+ "video",
+ "in_person",
+ "panel",
+ "technical",
+ "take_home"
+ ]
+ },
+ "public.job_status": {
+ "name": "job_status",
+ "schema": "public",
+ "values": [
+ "draft",
+ "open",
+ "closed",
+ "archived"
+ ]
+ },
+ "public.job_type": {
+ "name": "job_type",
+ "schema": "public",
+ "values": [
+ "full_time",
+ "part_time",
+ "contract",
+ "internship"
+ ]
+ },
+ "public.join_request_status": {
+ "name": "join_request_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "approved",
+ "rejected"
+ ]
+ },
+ "public.name_display_format": {
+ "name": "name_display_format",
+ "schema": "public",
+ "values": [
+ "first_last",
+ "last_first"
+ ]
+ },
+ "public.notification_cadence": {
+ "name": "notification_cadence",
+ "schema": "public",
+ "values": [
+ "instant",
+ "digest"
+ ]
+ },
+ "public.notification_channel_mode": {
+ "name": "notification_channel_mode",
+ "schema": "public",
+ "values": [
+ "instant",
+ "digest",
+ "off"
+ ]
+ },
+ "public.notification_outbox_status": {
+ "name": "notification_outbox_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "sent",
+ "skipped",
+ "dead"
+ ]
+ },
+ "public.notification_type": {
+ "name": "notification_type",
+ "schema": "public",
+ "values": [
+ "candidate_replied",
+ "application_created",
+ "analysis_completed"
+ ]
+ },
+ "public.property_entity_type": {
+ "name": "property_entity_type",
+ "schema": "public",
+ "values": [
+ "candidate",
+ "application"
+ ]
+ },
+ "public.property_type": {
+ "name": "property_type",
+ "schema": "public",
+ "values": [
+ "text",
+ "long_text",
+ "number",
+ "select",
+ "multi_select",
+ "date",
+ "checkbox",
+ "url",
+ "email",
+ "person",
+ "file"
+ ]
+ },
+ "public.question_type": {
+ "name": "question_type",
+ "schema": "public",
+ "values": [
+ "short_text",
+ "long_text",
+ "single_select",
+ "multi_select",
+ "number",
+ "date",
+ "url",
+ "checkbox",
+ "file_upload"
+ ]
+ },
+ "public.retention_audit_action": {
+ "name": "retention_audit_action",
+ "schema": "public",
+ "values": [
+ "quarantined",
+ "restored",
+ "erased",
+ "exempted",
+ "unexempted",
+ "exported"
+ ]
+ },
+ "public.source_channel": {
+ "name": "source_channel",
+ "schema": "public",
+ "values": [
+ "linkedin",
+ "indeed",
+ "glassdoor",
+ "ziprecruiter",
+ "monster",
+ "handshake",
+ "angellist",
+ "wellfound",
+ "dice",
+ "stackoverflow",
+ "weworkremotely",
+ "remoteok",
+ "builtin",
+ "hired",
+ "lever",
+ "greenhouse_board",
+ "google_jobs",
+ "facebook",
+ "twitter",
+ "instagram",
+ "tiktok",
+ "reddit",
+ "referral",
+ "career_site",
+ "email",
+ "event",
+ "agency",
+ "direct",
+ "other",
+ "custom"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/server/database/migrations/meta/0049_snapshot.json b/server/database/migrations/meta/0049_snapshot.json
new file mode 100644
index 00000000..4bf9640f
--- /dev/null
+++ b/server/database/migrations/meta/0049_snapshot.json
@@ -0,0 +1,7850 @@
+{
+ "id": "987f8813-63f4-4dfa-8118-1355299d1847",
+ "prevId": "5af28487-0a51-4974-8336-069747b058ef",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "account_user_id_idx": {
+ "name": "account_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invitation": {
+ "name": "invitation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "inviter_id": {
+ "name": "inviter_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "invitation_organization_id_idx": {
+ "name": "invitation_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invitation_email_idx": {
+ "name": "invitation_email_idx",
+ "columns": [
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invitation_inviter_id_user_id_fk": {
+ "name": "invitation_inviter_id_user_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "user",
+ "columnsFrom": [
+ "inviter_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invitation_organization_id_organization_id_fk": {
+ "name": "invitation_organization_id_organization_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.member": {
+ "name": "member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'member'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "member_user_id_idx": {
+ "name": "member_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "member_organization_id_idx": {
+ "name": "member_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "member_user_org_unique_idx": {
+ "name": "member_user_org_unique_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "member_user_id_user_id_fk": {
+ "name": "member_user_id_user_id_fk",
+ "tableFrom": "member",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "member_organization_id_organization_id_fk": {
+ "name": "member_organization_id_organization_id_fk",
+ "tableFrom": "member",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization": {
+ "name": "organization",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_slug_unique": {
+ "name": "organization_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.rate_limit": {
+ "name": "rate_limit",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "count": {
+ "name": "count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_request": {
+ "name": "last_request",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "rate_limit_key_idx": {
+ "name": "rate_limit_key_idx",
+ "columns": [
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "session_user_id_idx": {
+ "name": "session_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.subscription": {
+ "name": "subscription",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "plan": {
+ "name": "plan",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reference_id": {
+ "name": "reference_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_subscription_id": {
+ "name": "stripe_subscription_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'incomplete'"
+ },
+ "period_start": {
+ "name": "period_start",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "period_end": {
+ "name": "period_end",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trial_start": {
+ "name": "trial_start",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trial_end": {
+ "name": "trial_end",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancel_at_period_end": {
+ "name": "cancel_at_period_end",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "cancel_at": {
+ "name": "cancel_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "canceled_at": {
+ "name": "canceled_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ended_at": {
+ "name": "ended_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "seats": {
+ "name": "seats",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "billing_interval": {
+ "name": "billing_interval",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_schedule_id": {
+ "name": "stripe_schedule_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "subscription_reference_id_idx": {
+ "name": "subscription_reference_id_idx",
+ "columns": [
+ {
+ "expression": "reference_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "subscription_stripe_customer_id_idx": {
+ "name": "subscription_stripe_customer_id_idx",
+ "columns": [
+ {
+ "expression": "stripe_customer_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "verification_identifier_idx": {
+ "name": "verification_identifier_idx",
+ "columns": [
+ {
+ "expression": "identifier",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.activity_log": {
+ "name": "activity_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "activity_action",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_type": {
+ "name": "resource_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_id": {
+ "name": "resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "activity_log_organization_id_idx": {
+ "name": "activity_log_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_actor_id_idx": {
+ "name": "activity_log_actor_id_idx",
+ "columns": [
+ {
+ "expression": "actor_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_resource_idx": {
+ "name": "activity_log_resource_idx",
+ "columns": [
+ {
+ "expression": "resource_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "resource_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_created_at_idx": {
+ "name": "activity_log_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "activity_log_organization_id_organization_id_fk": {
+ "name": "activity_log_organization_id_organization_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "activity_log_actor_id_user_id_fk": {
+ "name": "activity_log_actor_id_user_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "user",
+ "columnsFrom": [
+ "actor_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ai_config": {
+ "name": "ai_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Default'"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openai'"
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'gpt-4o-mini'"
+ },
+ "api_key_encrypted": {
+ "name": "api_key_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 4096
+ },
+ "input_price_per_1m": {
+ "name": "input_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output_price_per_1m": {
+ "name": "output_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default_chatbot": {
+ "name": "is_default_chatbot",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_default_analysis": {
+ "name": "is_default_analysis",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ai_config_organization_id_idx": {
+ "name": "ai_config_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ai_config_default_chatbot_idx": {
+ "name": "ai_config_default_chatbot_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"ai_config\".\"is_default_chatbot\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ai_config_default_analysis_idx": {
+ "name": "ai_config_default_analysis_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"ai_config\".\"is_default_analysis\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ai_config_organization_id_organization_id_fk": {
+ "name": "ai_config_organization_id_organization_id_fk",
+ "tableFrom": "ai_config",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.analysis_run": {
+ "name": "analysis_run",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "analysis_run_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'completed'"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "criteria_snapshot": {
+ "name": "criteria_snapshot",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "composite_score": {
+ "name": "composite_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "prompt_tokens": {
+ "name": "prompt_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completion_tokens": {
+ "name": "completion_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cost_usd_micros": {
+ "name": "cost_usd_micros",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "billing_mode": {
+ "name": "billing_mode",
+ "type": "analysis_billing_mode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'byok'"
+ },
+ "raw_response": {
+ "name": "raw_response",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scored_by_id": {
+ "name": "scored_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "analysis_run_organization_id_idx": {
+ "name": "analysis_run_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "analysis_run_application_id_idx": {
+ "name": "analysis_run_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "analysis_run_created_at_idx": {
+ "name": "analysis_run_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "analysis_run_organization_id_organization_id_fk": {
+ "name": "analysis_run_organization_id_organization_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "analysis_run_application_id_application_id_fk": {
+ "name": "analysis_run_application_id_application_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "analysis_run_scored_by_id_user_id_fk": {
+ "name": "analysis_run_scored_by_id_user_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "user",
+ "columnsFrom": [
+ "scored_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.application": {
+ "name": "application",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "application_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'new'"
+ },
+ "score": {
+ "name": "score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cover_letter_text": {
+ "name": "cover_letter_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "application_organization_id_idx": {
+ "name": "application_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_candidate_id_idx": {
+ "name": "application_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_job_id_idx": {
+ "name": "application_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_org_candidate_job_idx": {
+ "name": "application_org_candidate_job_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "application_organization_id_organization_id_fk": {
+ "name": "application_organization_id_organization_id_fk",
+ "tableFrom": "application",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_candidate_id_candidate_id_fk": {
+ "name": "application_candidate_id_candidate_id_fk",
+ "tableFrom": "application",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_job_id_job_id_fk": {
+ "name": "application_job_id_job_id_fk",
+ "tableFrom": "application",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.application_source": {
+ "name": "application_source",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "source_channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'direct'"
+ },
+ "tracking_link_id": {
+ "name": "tracking_link_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_source": {
+ "name": "utm_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_medium": {
+ "name": "utm_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_campaign": {
+ "name": "utm_campaign",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_term": {
+ "name": "utm_term",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_content": {
+ "name": "utm_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "referrer_domain": {
+ "name": "referrer_domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "application_source_organization_id_idx": {
+ "name": "application_source_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_application_id_idx": {
+ "name": "application_source_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_channel_idx": {
+ "name": "application_source_channel_idx",
+ "columns": [
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_tracking_link_id_idx": {
+ "name": "application_source_tracking_link_id_idx",
+ "columns": [
+ {
+ "expression": "tracking_link_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_application_idx": {
+ "name": "application_source_application_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "application_source_organization_id_organization_id_fk": {
+ "name": "application_source_organization_id_organization_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_source_application_id_application_id_fk": {
+ "name": "application_source_application_id_application_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_source_tracking_link_id_tracking_link_id_fk": {
+ "name": "application_source_tracking_link_id_tracking_link_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "tracking_link",
+ "columnsFrom": [
+ "tracking_link_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.calendar_integration": {
+ "name": "calendar_integration",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "calendar_provider",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'google'"
+ },
+ "access_token_encrypted": {
+ "name": "access_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token_encrypted": {
+ "name": "refresh_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "calendar_id": {
+ "name": "calendar_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'primary'"
+ },
+ "account_email": {
+ "name": "account_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_channel_id": {
+ "name": "webhook_channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_resource_id": {
+ "name": "webhook_resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_expiration": {
+ "name": "webhook_expiration",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_token": {
+ "name": "sync_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "calendar_integration_user_provider_idx": {
+ "name": "calendar_integration_user_provider_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "calendar_integration_webhook_channel_idx": {
+ "name": "calendar_integration_webhook_channel_idx",
+ "columns": [
+ {
+ "expression": "webhook_channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "calendar_integration_user_id_user_id_fk": {
+ "name": "calendar_integration_user_id_user_id_fk",
+ "tableFrom": "calendar_integration",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate": {
+ "name": "candidate",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone": {
+ "name": "phone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gender": {
+ "name": "gender",
+ "type": "gender",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "date_of_birth": {
+ "name": "date_of_birth",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "quick_notes": {
+ "name": "quick_notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "retention_exempt_until": {
+ "name": "retention_exempt_until",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "retention_exempt_reason": {
+ "name": "retention_exempt_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "retention_reviewed_at": {
+ "name": "retention_reviewed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "quarantined_at": {
+ "name": "quarantined_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scheduled_purge_at": {
+ "name": "scheduled_purge_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_organization_id_idx": {
+ "name": "candidate_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_gender_idx": {
+ "name": "candidate_gender_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "gender",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_org_email_idx": {
+ "name": "candidate_org_email_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_quarantine_idx": {
+ "name": "candidate_quarantine_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scheduled_purge_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_organization_id_organization_id_fk": {
+ "name": "candidate_organization_id_organization_id_fk",
+ "tableFrom": "candidate",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_conversation": {
+ "name": "candidate_conversation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reply_token": {
+ "name": "reply_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "unread_count": {
+ "name": "unread_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_message_at": {
+ "name": "last_message_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_conversation_organization_id_idx": {
+ "name": "candidate_conversation_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_conversation_application_id_idx": {
+ "name": "candidate_conversation_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_conversation_reply_token_idx": {
+ "name": "candidate_conversation_reply_token_idx",
+ "columns": [
+ {
+ "expression": "reply_token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_conversation_last_message_at_idx": {
+ "name": "candidate_conversation_last_message_at_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "last_message_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_conversation_organization_id_organization_id_fk": {
+ "name": "candidate_conversation_organization_id_organization_id_fk",
+ "tableFrom": "candidate_conversation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_conversation_application_id_application_id_fk": {
+ "name": "candidate_conversation_application_id_application_id_fk",
+ "tableFrom": "candidate_conversation",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_message": {
+ "name": "candidate_message",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "conversation_id": {
+ "name": "conversation_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "direction": {
+ "name": "direction",
+ "type": "candidate_message_direction",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "candidate_message_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "from_email": {
+ "name": "from_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "to_email": {
+ "name": "to_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_text": {
+ "name": "body_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'message'"
+ },
+ "interview_id": {
+ "name": "interview_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "calendar_attachment_status": {
+ "name": "calendar_attachment_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'not_applicable'"
+ },
+ "calendar_attachment_error": {
+ "name": "calendar_attachment_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "calendar_sequence": {
+ "name": "calendar_sequence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "internet_message_id": {
+ "name": "internet_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "in_reply_to": {
+ "name": "in_reply_to",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "references": {
+ "name": "references",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_by_id": {
+ "name": "sent_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_status_at": {
+ "name": "provider_status_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_at": {
+ "name": "sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "delivered_at": {
+ "name": "delivered_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failed_at": {
+ "name": "failed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_code": {
+ "name": "error_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_message_organization_id_idx": {
+ "name": "candidate_message_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_conversation_id_idx": {
+ "name": "candidate_message_conversation_id_idx",
+ "columns": [
+ {
+ "expression": "conversation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_interview_id_idx": {
+ "name": "candidate_message_interview_id_idx",
+ "columns": [
+ {
+ "expression": "interview_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_provider_id_idx": {
+ "name": "candidate_message_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_internet_id_idx": {
+ "name": "candidate_message_internet_id_idx",
+ "columns": [
+ {
+ "expression": "internet_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_message_organization_id_organization_id_fk": {
+ "name": "candidate_message_organization_id_organization_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_message_conversation_id_candidate_conversation_id_fk": {
+ "name": "candidate_message_conversation_id_candidate_conversation_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "candidate_conversation",
+ "columnsFrom": [
+ "conversation_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_message_interview_id_interview_id_fk": {
+ "name": "candidate_message_interview_id_interview_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "interview",
+ "columnsFrom": [
+ "interview_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "candidate_message_sent_by_id_user_id_fk": {
+ "name": "candidate_message_sent_by_id_user_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "user",
+ "columnsFrom": [
+ "sent_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_message_attachment": {
+ "name": "candidate_message_attachment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message_id": {
+ "name": "message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "storage_key": {
+ "name": "storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_attachment_id": {
+ "name": "provider_attachment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_message_attachment_organization_id_idx": {
+ "name": "candidate_message_attachment_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_attachment_message_id_idx": {
+ "name": "candidate_message_attachment_message_id_idx",
+ "columns": [
+ {
+ "expression": "message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_message_attachment_organization_id_organization_id_fk": {
+ "name": "candidate_message_attachment_organization_id_organization_id_fk",
+ "tableFrom": "candidate_message_attachment",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_message_attachment_message_id_candidate_message_id_fk": {
+ "name": "candidate_message_attachment_message_id_candidate_message_id_fk",
+ "tableFrom": "candidate_message_attachment",
+ "tableTo": "candidate_message",
+ "columnsFrom": [
+ "message_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "candidate_message_attachment_storage_key_unique": {
+ "name": "candidate_message_attachment_storage_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "storage_key"
+ ]
+ },
+ "candidate_message_attachment_provider_attachment_id_unique": {
+ "name": "candidate_message_attachment_provider_attachment_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_attachment_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_message_webhook_event": {
+ "name": "candidate_message_webhook_event",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "occurred_at": {
+ "name": "occurred_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "processed_at": {
+ "name": "processed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_message_webhook_provider_id_idx": {
+ "name": "candidate_message_webhook_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_webhook_unprocessed_idx": {
+ "name": "candidate_message_webhook_unprocessed_idx",
+ "columns": [
+ {
+ "expression": "processed_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.career_page": {
+ "name": "career_page",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "accent_color": {
+ "name": "accent_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "headline": {
+ "name": "headline",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "logo_storage_key": {
+ "name": "logo_storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "banner_storage_key": {
+ "name": "banner_storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "banner_position": {
+ "name": "banner_position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "career_page_organization_id_idx": {
+ "name": "career_page_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "career_page_slug_idx": {
+ "name": "career_page_slug_idx",
+ "columns": [
+ {
+ "expression": "slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "career_page_organization_id_organization_id_fk": {
+ "name": "career_page_organization_id_organization_id_fk",
+ "tableFrom": "career_page",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_agent": {
+ "name": "chatbot_agent",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "system_prompt": {
+ "name": "system_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "temperature": {
+ "name": "temperature",
+ "type": "numeric(3, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_agent_org_user_idx": {
+ "name": "chatbot_agent_org_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "chatbot_agent_default_per_user_idx": {
+ "name": "chatbot_agent_default_per_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"chatbot_agent\".\"is_default\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_agent_organization_id_organization_id_fk": {
+ "name": "chatbot_agent_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_agent",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_agent_user_id_user_id_fk": {
+ "name": "chatbot_agent_user_id_user_id_fk",
+ "tableFrom": "chatbot_agent",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_conversation": {
+ "name": "chatbot_conversation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "folder_id": {
+ "name": "folder_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ai_config_id": {
+ "name": "ai_config_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'New chat'"
+ },
+ "scope": {
+ "name": "scope",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thinking": {
+ "name": "thinking",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "pinned": {
+ "name": "pinned",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "last_message_preview": {
+ "name": "last_message_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message_at": {
+ "name": "last_message_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_conversation_org_user_idx": {
+ "name": "chatbot_conversation_org_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "chatbot_conversation_folder_idx": {
+ "name": "chatbot_conversation_folder_idx",
+ "columns": [
+ {
+ "expression": "folder_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "chatbot_conversation_last_message_at_idx": {
+ "name": "chatbot_conversation_last_message_at_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "last_message_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_conversation_organization_id_organization_id_fk": {
+ "name": "chatbot_conversation_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_user_id_user_id_fk": {
+ "name": "chatbot_conversation_user_id_user_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_folder_id_chatbot_folder_id_fk": {
+ "name": "chatbot_conversation_folder_id_chatbot_folder_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "chatbot_folder",
+ "columnsFrom": [
+ "folder_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_agent_id_chatbot_agent_id_fk": {
+ "name": "chatbot_conversation_agent_id_chatbot_agent_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "chatbot_agent",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_ai_config_id_ai_config_id_fk": {
+ "name": "chatbot_conversation_ai_config_id_ai_config_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "ai_config",
+ "columnsFrom": [
+ "ai_config_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_folder": {
+ "name": "chatbot_folder",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "position": {
+ "name": "position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_folder_org_user_idx": {
+ "name": "chatbot_folder_org_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_folder_organization_id_organization_id_fk": {
+ "name": "chatbot_folder_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_folder",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_folder_user_id_user_id_fk": {
+ "name": "chatbot_folder_user_id_user_id_fk",
+ "tableFrom": "chatbot_folder",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_message": {
+ "name": "chatbot_message",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "conversation_id": {
+ "name": "conversation_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "chatbot_message_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "reasoning": {
+ "name": "reasoning",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tool_calls": {
+ "name": "tool_calls",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sources": {
+ "name": "sources",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attachments": {
+ "name": "attachments",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_message_conversation_idx": {
+ "name": "chatbot_message_conversation_idx",
+ "columns": [
+ {
+ "expression": "conversation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_message_conversation_id_chatbot_conversation_id_fk": {
+ "name": "chatbot_message_conversation_id_chatbot_conversation_id_fk",
+ "tableFrom": "chatbot_message",
+ "tableTo": "chatbot_conversation",
+ "columnsFrom": [
+ "conversation_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_message_organization_id_organization_id_fk": {
+ "name": "chatbot_message_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_message",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_message_user_id_user_id_fk": {
+ "name": "chatbot_message_user_id_user_id_fk",
+ "tableFrom": "chatbot_message",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.comment": {
+ "name": "comment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_id": {
+ "name": "author_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "comment_target",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "comment_organization_id_idx": {
+ "name": "comment_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_target_idx": {
+ "name": "comment_target_idx",
+ "columns": [
+ {
+ "expression": "target_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_author_id_idx": {
+ "name": "comment_author_id_idx",
+ "columns": [
+ {
+ "expression": "author_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "comment_organization_id_organization_id_fk": {
+ "name": "comment_organization_id_organization_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_author_id_user_id_fk": {
+ "name": "comment_author_id_user_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "user",
+ "columnsFrom": [
+ "author_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.criterion_score": {
+ "name": "criterion_score",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "criterion_key": {
+ "name": "criterion_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "max_score": {
+ "name": "max_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "applicant_score": {
+ "name": "applicant_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "confidence": {
+ "name": "confidence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "evidence": {
+ "name": "evidence",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "strengths": {
+ "name": "strengths",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gaps": {
+ "name": "gaps",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "criterion_score_organization_id_idx": {
+ "name": "criterion_score_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "criterion_score_application_id_idx": {
+ "name": "criterion_score_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "criterion_score_app_criterion_idx": {
+ "name": "criterion_score_app_criterion_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "criterion_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "criterion_score_organization_id_organization_id_fk": {
+ "name": "criterion_score_organization_id_organization_id_fk",
+ "tableFrom": "criterion_score",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "criterion_score_application_id_application_id_fk": {
+ "name": "criterion_score_application_id_application_id_fk",
+ "tableFrom": "criterion_score",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.document": {
+ "name": "document",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "document_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'resume'"
+ },
+ "storage_key": {
+ "name": "storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "original_filename": {
+ "name": "original_filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "parsed_content": {
+ "name": "parsed_content",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "document_organization_id_idx": {
+ "name": "document_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "document_candidate_id_idx": {
+ "name": "document_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "document_organization_id_organization_id_fk": {
+ "name": "document_organization_id_organization_id_fk",
+ "tableFrom": "document",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "document_candidate_id_candidate_id_fk": {
+ "name": "document_candidate_id_candidate_id_fk",
+ "tableFrom": "document",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "document_storage_key_unique": {
+ "name": "document_storage_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "storage_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_suppression": {
+ "name": "email_suppression",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "email_suppression_reason",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_suppression_email_idx": {
+ "name": "email_suppression_email_idx",
+ "columns": [
+ {
+ "expression": "lower(\"email\")",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_template": {
+ "name": "email_template",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_template_organization_id_idx": {
+ "name": "email_template_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "email_template_created_by_id_idx": {
+ "name": "email_template_created_by_id_idx",
+ "columns": [
+ {
+ "expression": "created_by_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_template_organization_id_organization_id_fk": {
+ "name": "email_template_organization_id_organization_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "email_template_created_by_id_user_id_fk": {
+ "name": "email_template_created_by_id_user_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.import_job": {
+ "name": "import_job",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "import_source",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "import_job_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'processing'"
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mapping": {
+ "name": "mapping",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "target_job_id": {
+ "name": "target_job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_rows": {
+ "name": "total_rows",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "committed_rows": {
+ "name": "committed_rows",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "import_job_organization_id_idx": {
+ "name": "import_job_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "import_job_status_idx": {
+ "name": "import_job_status_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "import_job_organization_id_organization_id_fk": {
+ "name": "import_job_organization_id_organization_id_fk",
+ "tableFrom": "import_job",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_job_created_by_user_id_fk": {
+ "name": "import_job_created_by_user_id_fk",
+ "tableFrom": "import_job",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_job_target_job_id_job_id_fk": {
+ "name": "import_job_target_job_id_job_id_fk",
+ "tableFrom": "import_job",
+ "tableTo": "job",
+ "columnsFrom": [
+ "target_job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.import_row": {
+ "name": "import_row",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "row_index": {
+ "name": "row_index",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "raw_data": {
+ "name": "raw_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_data": {
+ "name": "normalized_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "import_row_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'ready'"
+ },
+ "dedupe_hash": {
+ "name": "dedupe_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "matched_candidate_id": {
+ "name": "matched_candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_candidate_id": {
+ "name": "created_candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "import_row_job_id_idx": {
+ "name": "import_row_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "import_row_job_status_idx": {
+ "name": "import_row_job_status_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "import_row_dedupe_idx": {
+ "name": "import_row_dedupe_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "dedupe_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "import_row_organization_id_organization_id_fk": {
+ "name": "import_row_organization_id_organization_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_row_job_id_import_job_id_fk": {
+ "name": "import_row_job_id_import_job_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "import_job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_row_matched_candidate_id_candidate_id_fk": {
+ "name": "import_row_matched_candidate_id_candidate_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "matched_candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "import_row_created_candidate_id_candidate_id_fk": {
+ "name": "import_row_created_candidate_id_candidate_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "created_candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.interview": {
+ "name": "interview",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "interview_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'video'"
+ },
+ "status": {
+ "name": "status",
+ "type": "interview_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'scheduled'"
+ },
+ "scheduled_at": {
+ "name": "scheduled_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "duration": {
+ "name": "duration",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 60
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "personal_note": {
+ "name": "personal_note",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "interviewers": {
+ "name": "interviewers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "invitation_sent_at": {
+ "name": "invitation_sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "candidate_response": {
+ "name": "candidate_response",
+ "type": "candidate_response",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "candidate_responded_at": {
+ "name": "candidate_responded_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_calendar_event_id": {
+ "name": "google_calendar_event_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_calendar_event_link": {
+ "name": "google_calendar_event_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'UTC'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "interview_organization_id_idx": {
+ "name": "interview_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_application_id_idx": {
+ "name": "interview_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_scheduled_at_idx": {
+ "name": "interview_scheduled_at_idx",
+ "columns": [
+ {
+ "expression": "scheduled_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_status_idx": {
+ "name": "interview_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_created_by_id_idx": {
+ "name": "interview_created_by_id_idx",
+ "columns": [
+ {
+ "expression": "created_by_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "interview_organization_id_organization_id_fk": {
+ "name": "interview_organization_id_organization_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "interview_application_id_application_id_fk": {
+ "name": "interview_application_id_application_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "interview_created_by_id_user_id_fk": {
+ "name": "interview_created_by_id_user_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invite_link": {
+ "name": "invite_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'member'"
+ },
+ "max_uses": {
+ "name": "max_uses",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "use_count": {
+ "name": "use_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "invite_link_organization_id_idx": {
+ "name": "invite_link_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invite_link_token_idx": {
+ "name": "invite_link_token_idx",
+ "columns": [
+ {
+ "expression": "token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invite_link_organization_id_organization_id_fk": {
+ "name": "invite_link_organization_id_organization_id_fk",
+ "tableFrom": "invite_link",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invite_link_created_by_id_user_id_fk": {
+ "name": "invite_link_created_by_id_user_id_fk",
+ "tableFrom": "invite_link",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "invite_link_token_unique": {
+ "name": "invite_link_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job": {
+ "name": "job",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "job_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'full_time'"
+ },
+ "status": {
+ "name": "status",
+ "type": "job_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "salary_min": {
+ "name": "salary_min",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_max": {
+ "name": "salary_max",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_currency": {
+ "name": "salary_currency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_unit": {
+ "name": "salary_unit",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_negotiable": {
+ "name": "salary_negotiable",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "remote_status": {
+ "name": "remote_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "valid_through": {
+ "name": "valid_through",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "experience_level": {
+ "name": "experience_level",
+ "type": "experience_level",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "phone_requirement": {
+ "name": "phone_requirement",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'optional'"
+ },
+ "require_resume": {
+ "name": "require_resume",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "require_cover_letter": {
+ "name": "require_cover_letter",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "auto_score_on_apply": {
+ "name": "auto_score_on_apply",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "analysis_context": {
+ "name": "analysis_context",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{\"coverLetter\":true,\"screeningAnswers\":true,\"recruiterNotes\":false}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "job_organization_id_idx": {
+ "name": "job_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_organization_id_organization_id_fk": {
+ "name": "job_organization_id_organization_id_fk",
+ "tableFrom": "job",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "job_slug_unique": {
+ "name": "job_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job_question": {
+ "name": "job_question",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "question_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'short_text'"
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "required": {
+ "name": "required",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "options": {
+ "name": "options",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "job_question_organization_id_idx": {
+ "name": "job_question_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "job_question_job_id_idx": {
+ "name": "job_question_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_question_organization_id_organization_id_fk": {
+ "name": "job_question_organization_id_organization_id_fk",
+ "tableFrom": "job_question",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "job_question_job_id_job_id_fk": {
+ "name": "job_question_job_id_job_id_fk",
+ "tableFrom": "job_question",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.join_request": {
+ "name": "join_request",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "join_request_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "reviewed_by_id": {
+ "name": "reviewed_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reviewed_at": {
+ "name": "reviewed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "join_request_organization_id_idx": {
+ "name": "join_request_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "join_request_user_id_idx": {
+ "name": "join_request_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "join_request_status_idx": {
+ "name": "join_request_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "join_request_user_id_user_id_fk": {
+ "name": "join_request_user_id_user_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "join_request_organization_id_organization_id_fk": {
+ "name": "join_request_organization_id_organization_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "join_request_reviewed_by_id_user_id_fk": {
+ "name": "join_request_reviewed_by_id_user_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "user",
+ "columnsFrom": [
+ "reviewed_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification_outbox": {
+ "name": "notification_outbox",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipient_user_id": {
+ "name": "recipient_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipient_email": {
+ "name": "recipient_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "notification_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cadence": {
+ "name": "cadence",
+ "type": "notification_cadence",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "notification_outbox_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempts": {
+ "name": "attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_attempt_at": {
+ "name": "next_attempt_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "digest_bucket": {
+ "name": "digest_bucket",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_at": {
+ "name": "sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failed_at": {
+ "name": "failed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "notification_outbox_dedupe_key_idx": {
+ "name": "notification_outbox_dedupe_key_idx",
+ "columns": [
+ {
+ "expression": "dedupe_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_pull_idx": {
+ "name": "notification_outbox_pull_idx",
+ "columns": [
+ {
+ "expression": "cadence",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_attempt_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"notification_outbox\".\"status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_provider_id_idx": {
+ "name": "notification_outbox_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"notification_outbox\".\"provider_message_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_organization_id_idx": {
+ "name": "notification_outbox_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_digest_idx": {
+ "name": "notification_outbox_digest_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "recipient_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "digest_bucket",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "notification_outbox_organization_id_organization_id_fk": {
+ "name": "notification_outbox_organization_id_organization_id_fk",
+ "tableFrom": "notification_outbox",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_outbox_recipient_user_id_user_id_fk": {
+ "name": "notification_outbox_recipient_user_id_user_id_fk",
+ "tableFrom": "notification_outbox",
+ "tableTo": "user",
+ "columnsFrom": [
+ "recipient_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification_preference": {
+ "name": "notification_preference",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "notification_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_mode": {
+ "name": "channel_mode",
+ "type": "notification_channel_mode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "notification_preference_user_org_type_idx": {
+ "name": "notification_preference_user_org_type_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_preference_organization_id_idx": {
+ "name": "notification_preference_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "notification_preference_user_id_user_id_fk": {
+ "name": "notification_preference_user_id_user_id_fk",
+ "tableFrom": "notification_preference",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_preference_organization_id_organization_id_fk": {
+ "name": "notification_preference_organization_id_organization_id_fk",
+ "tableFrom": "notification_preference",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.onboarding_survey_response": {
+ "name": "onboarding_survey_response",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "signup_plan": {
+ "name": "signup_plan",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "signup_billing": {
+ "name": "signup_billing",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "company_size": {
+ "name": "company_size",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_role": {
+ "name": "user_role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discovery_source": {
+ "name": "discovery_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "current_hiring_process": {
+ "name": "current_hiring_process",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expected_roles_12m": {
+ "name": "expected_roles_12m",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "answered_count": {
+ "name": "answered_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "skipped_count": {
+ "name": "skipped_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "onboarding_survey_response_user_id_idx": {
+ "name": "onboarding_survey_response_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "onboarding_survey_response_organization_id_idx": {
+ "name": "onboarding_survey_response_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "onboarding_survey_response_user_id_user_id_fk": {
+ "name": "onboarding_survey_response_user_id_user_id_fk",
+ "tableFrom": "onboarding_survey_response",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "onboarding_survey_response_organization_id_organization_id_fk": {
+ "name": "onboarding_survey_response_organization_id_organization_id_fk",
+ "tableFrom": "onboarding_survey_response",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.org_settings": {
+ "name": "org_settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name_display_format": {
+ "name": "name_display_format",
+ "type": "name_display_format",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'first_last'"
+ },
+ "date_format": {
+ "name": "date_format",
+ "type": "date_format",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'mdy'"
+ },
+ "retention_enabled": {
+ "name": "retention_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "retention_months": {
+ "name": "retention_months",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 24
+ },
+ "quarantine_days": {
+ "name": "quarantine_days",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 30
+ },
+ "retention_activated_at": {
+ "name": "retention_activated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "privacy_policy_url": {
+ "name": "privacy_policy_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "privacy_policy_text": {
+ "name": "privacy_policy_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "privacy_contact_email": {
+ "name": "privacy_contact_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "org_settings_organization_id_idx": {
+ "name": "org_settings_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "org_settings_organization_id_organization_id_fk": {
+ "name": "org_settings_organization_id_organization_id_fk",
+ "tableFrom": "org_settings",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.platform_ai_config": {
+ "name": "platform_ai_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Platform (OpenRouter)'"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openrouter'"
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openai/gpt-5.4-mini'"
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 4096
+ },
+ "input_price_per_1m": {
+ "name": "input_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output_price_per_1m": {
+ "name": "output_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default_analysis": {
+ "name": "is_default_analysis",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "is_enabled": {
+ "name": "is_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "platform_ai_config_organization_id_idx": {
+ "name": "platform_ai_config_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "platform_ai_config_organization_id_organization_id_fk": {
+ "name": "platform_ai_config_organization_id_organization_id_fk",
+ "tableFrom": "platform_ai_config",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.property_definition": {
+ "name": "property_definition",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "property_entity_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "property_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "property_definition_org_idx": {
+ "name": "property_definition_org_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_definition_org_entity_idx": {
+ "name": "property_definition_org_entity_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_definition_job_idx": {
+ "name": "property_definition_job_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "property_definition_organization_id_organization_id_fk": {
+ "name": "property_definition_organization_id_organization_id_fk",
+ "tableFrom": "property_definition",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "property_definition_job_id_job_id_fk": {
+ "name": "property_definition_job_id_job_id_fk",
+ "tableFrom": "property_definition",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.property_value": {
+ "name": "property_value",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "property_definition_id": {
+ "name": "property_definition_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "property_entity_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entity_id": {
+ "name": "entity_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "property_value_org_idx": {
+ "name": "property_value_org_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_value_entity_idx": {
+ "name": "property_value_entity_idx",
+ "columns": [
+ {
+ "expression": "entity_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_value_definition_idx": {
+ "name": "property_value_definition_idx",
+ "columns": [
+ {
+ "expression": "property_definition_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_value_def_entity_idx": {
+ "name": "property_value_def_entity_idx",
+ "columns": [
+ {
+ "expression": "property_definition_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "property_value_organization_id_organization_id_fk": {
+ "name": "property_value_organization_id_organization_id_fk",
+ "tableFrom": "property_value",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "property_value_property_definition_id_property_definition_id_fk": {
+ "name": "property_value_property_definition_id_property_definition_id_fk",
+ "tableFrom": "property_value",
+ "tableTo": "property_definition",
+ "columnsFrom": [
+ "property_definition_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.question_response": {
+ "name": "question_response",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "question_id": {
+ "name": "question_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "question_response_organization_id_idx": {
+ "name": "question_response_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "question_response_application_id_idx": {
+ "name": "question_response_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "question_response_question_id_idx": {
+ "name": "question_response_question_id_idx",
+ "columns": [
+ {
+ "expression": "question_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "question_response_organization_id_organization_id_fk": {
+ "name": "question_response_organization_id_organization_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "question_response_application_id_application_id_fk": {
+ "name": "question_response_application_id_application_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "question_response_question_id_job_question_id_fk": {
+ "name": "question_response_question_id_job_question_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "job_question",
+ "columnsFrom": [
+ "question_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.retention_audit": {
+ "name": "retention_audit",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "retention_audit_action",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "result": {
+ "name": "result",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'success'"
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "retention_audit_organization_id_idx": {
+ "name": "retention_audit_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "retention_audit_candidate_id_idx": {
+ "name": "retention_audit_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "retention_audit_created_at_idx": {
+ "name": "retention_audit_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "retention_audit_organization_id_organization_id_fk": {
+ "name": "retention_audit_organization_id_organization_id_fk",
+ "tableFrom": "retention_audit",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scoring_criterion": {
+ "name": "scoring_criterion",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "category": {
+ "name": "category",
+ "type": "criterion_category",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'custom'"
+ },
+ "max_score": {
+ "name": "max_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 10
+ },
+ "weight": {
+ "name": "weight",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scoring_criterion_organization_id_idx": {
+ "name": "scoring_criterion_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scoring_criterion_job_id_idx": {
+ "name": "scoring_criterion_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scoring_criterion_job_key_idx": {
+ "name": "scoring_criterion_job_key_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scoring_criterion_organization_id_organization_id_fk": {
+ "name": "scoring_criterion_organization_id_organization_id_fk",
+ "tableFrom": "scoring_criterion",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "scoring_criterion_job_id_job_id_fk": {
+ "name": "scoring_criterion_job_id_job_id_fk",
+ "tableFrom": "scoring_criterion",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tracking_link": {
+ "name": "tracking_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "channel": {
+ "name": "channel",
+ "type": "source_channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'custom'"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code": {
+ "name": "code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "utm_source": {
+ "name": "utm_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_medium": {
+ "name": "utm_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_campaign": {
+ "name": "utm_campaign",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_term": {
+ "name": "utm_term",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_content": {
+ "name": "utm_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "click_count": {
+ "name": "click_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "application_count": {
+ "name": "application_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "tracking_link_organization_id_idx": {
+ "name": "tracking_link_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_job_id_idx": {
+ "name": "tracking_link_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_code_idx": {
+ "name": "tracking_link_code_idx",
+ "columns": [
+ {
+ "expression": "code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_channel_idx": {
+ "name": "tracking_link_channel_idx",
+ "columns": [
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tracking_link_organization_id_organization_id_fk": {
+ "name": "tracking_link_organization_id_organization_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tracking_link_job_id_job_id_fk": {
+ "name": "tracking_link_job_id_job_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tracking_link_created_by_id_user_id_fk": {
+ "name": "tracking_link_created_by_id_user_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "tracking_link_code_unique": {
+ "name": "tracking_link_code_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "code"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sso_provider": {
+ "name": "sso_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oidc_config": {
+ "name": "oidc_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "saml_config": {
+ "name": "saml_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "sso_provider_domain_idx": {
+ "name": "sso_provider_domain_idx",
+ "columns": [
+ {
+ "expression": "domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "sso_provider_provider_id_idx": {
+ "name": "sso_provider_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "sso_provider_organization_id_idx": {
+ "name": "sso_provider_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "sso_provider_user_id_user_id_fk": {
+ "name": "sso_provider_user_id_user_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "sso_provider_organization_id_organization_id_fk": {
+ "name": "sso_provider_organization_id_organization_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.activity_action": {
+ "name": "activity_action",
+ "schema": "public",
+ "values": [
+ "created",
+ "updated",
+ "deleted",
+ "status_changed",
+ "comment_added",
+ "member_invited",
+ "member_removed",
+ "member_role_changed",
+ "scored"
+ ]
+ },
+ "public.analysis_billing_mode": {
+ "name": "analysis_billing_mode",
+ "schema": "public",
+ "values": [
+ "platform",
+ "byok"
+ ]
+ },
+ "public.analysis_run_status": {
+ "name": "analysis_run_status",
+ "schema": "public",
+ "values": [
+ "completed",
+ "failed",
+ "partial"
+ ]
+ },
+ "public.application_status": {
+ "name": "application_status",
+ "schema": "public",
+ "values": [
+ "new",
+ "screening",
+ "interview",
+ "offer",
+ "hired",
+ "rejected"
+ ]
+ },
+ "public.calendar_provider": {
+ "name": "calendar_provider",
+ "schema": "public",
+ "values": [
+ "google"
+ ]
+ },
+ "public.candidate_message_direction": {
+ "name": "candidate_message_direction",
+ "schema": "public",
+ "values": [
+ "inbound",
+ "outbound"
+ ]
+ },
+ "public.candidate_message_status": {
+ "name": "candidate_message_status",
+ "schema": "public",
+ "values": [
+ "queued",
+ "sent",
+ "delivered",
+ "delayed",
+ "bounced",
+ "failed",
+ "complained"
+ ]
+ },
+ "public.candidate_response": {
+ "name": "candidate_response",
+ "schema": "public",
+ "values": [
+ "pending",
+ "accepted",
+ "declined",
+ "tentative"
+ ]
+ },
+ "public.chatbot_message_role": {
+ "name": "chatbot_message_role",
+ "schema": "public",
+ "values": [
+ "user",
+ "assistant"
+ ]
+ },
+ "public.comment_target": {
+ "name": "comment_target",
+ "schema": "public",
+ "values": [
+ "candidate",
+ "application",
+ "job"
+ ]
+ },
+ "public.criterion_category": {
+ "name": "criterion_category",
+ "schema": "public",
+ "values": [
+ "technical",
+ "experience",
+ "soft_skills",
+ "education",
+ "culture",
+ "custom"
+ ]
+ },
+ "public.date_format": {
+ "name": "date_format",
+ "schema": "public",
+ "values": [
+ "mdy",
+ "dmy",
+ "ymd"
+ ]
+ },
+ "public.document_type": {
+ "name": "document_type",
+ "schema": "public",
+ "values": [
+ "resume",
+ "cover_letter",
+ "other"
+ ]
+ },
+ "public.email_suppression_reason": {
+ "name": "email_suppression_reason",
+ "schema": "public",
+ "values": [
+ "bounce",
+ "complaint"
+ ]
+ },
+ "public.experience_level": {
+ "name": "experience_level",
+ "schema": "public",
+ "values": [
+ "junior",
+ "mid",
+ "senior",
+ "lead"
+ ]
+ },
+ "public.gender": {
+ "name": "gender",
+ "schema": "public",
+ "values": [
+ "male",
+ "female",
+ "other",
+ "prefer_not_to_say"
+ ]
+ },
+ "public.import_job_status": {
+ "name": "import_job_status",
+ "schema": "public",
+ "values": [
+ "processing",
+ "previewing",
+ "committing",
+ "completed",
+ "failed"
+ ]
+ },
+ "public.import_row_status": {
+ "name": "import_row_status",
+ "schema": "public",
+ "values": [
+ "ready",
+ "duplicate",
+ "duplicate_in_file",
+ "error",
+ "committed",
+ "skipped"
+ ]
+ },
+ "public.import_source": {
+ "name": "import_source",
+ "schema": "public",
+ "values": [
+ "csv",
+ "xlsx",
+ "resume_zip"
+ ]
+ },
+ "public.interview_status": {
+ "name": "interview_status",
+ "schema": "public",
+ "values": [
+ "scheduled",
+ "completed",
+ "cancelled",
+ "no_show"
+ ]
+ },
+ "public.interview_type": {
+ "name": "interview_type",
+ "schema": "public",
+ "values": [
+ "phone",
+ "video",
+ "in_person",
+ "panel",
+ "technical",
+ "take_home"
+ ]
+ },
+ "public.job_status": {
+ "name": "job_status",
+ "schema": "public",
+ "values": [
+ "draft",
+ "open",
+ "closed",
+ "archived"
+ ]
+ },
+ "public.job_type": {
+ "name": "job_type",
+ "schema": "public",
+ "values": [
+ "full_time",
+ "part_time",
+ "contract",
+ "internship"
+ ]
+ },
+ "public.join_request_status": {
+ "name": "join_request_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "approved",
+ "rejected"
+ ]
+ },
+ "public.name_display_format": {
+ "name": "name_display_format",
+ "schema": "public",
+ "values": [
+ "first_last",
+ "last_first"
+ ]
+ },
+ "public.notification_cadence": {
+ "name": "notification_cadence",
+ "schema": "public",
+ "values": [
+ "instant",
+ "digest"
+ ]
+ },
+ "public.notification_channel_mode": {
+ "name": "notification_channel_mode",
+ "schema": "public",
+ "values": [
+ "instant",
+ "digest",
+ "off"
+ ]
+ },
+ "public.notification_outbox_status": {
+ "name": "notification_outbox_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "sent",
+ "skipped",
+ "dead"
+ ]
+ },
+ "public.notification_type": {
+ "name": "notification_type",
+ "schema": "public",
+ "values": [
+ "candidate_replied",
+ "application_created",
+ "analysis_completed",
+ "interview_response"
+ ]
+ },
+ "public.property_entity_type": {
+ "name": "property_entity_type",
+ "schema": "public",
+ "values": [
+ "candidate",
+ "application"
+ ]
+ },
+ "public.property_type": {
+ "name": "property_type",
+ "schema": "public",
+ "values": [
+ "text",
+ "long_text",
+ "number",
+ "select",
+ "multi_select",
+ "date",
+ "checkbox",
+ "url",
+ "email",
+ "person",
+ "file"
+ ]
+ },
+ "public.question_type": {
+ "name": "question_type",
+ "schema": "public",
+ "values": [
+ "short_text",
+ "long_text",
+ "single_select",
+ "multi_select",
+ "number",
+ "date",
+ "url",
+ "checkbox",
+ "file_upload"
+ ]
+ },
+ "public.retention_audit_action": {
+ "name": "retention_audit_action",
+ "schema": "public",
+ "values": [
+ "quarantined",
+ "restored",
+ "erased",
+ "exempted",
+ "unexempted",
+ "exported"
+ ]
+ },
+ "public.source_channel": {
+ "name": "source_channel",
+ "schema": "public",
+ "values": [
+ "linkedin",
+ "indeed",
+ "glassdoor",
+ "ziprecruiter",
+ "monster",
+ "handshake",
+ "angellist",
+ "wellfound",
+ "dice",
+ "stackoverflow",
+ "weworkremotely",
+ "remoteok",
+ "builtin",
+ "hired",
+ "lever",
+ "greenhouse_board",
+ "google_jobs",
+ "facebook",
+ "twitter",
+ "instagram",
+ "tiktok",
+ "reddit",
+ "referral",
+ "career_site",
+ "email",
+ "event",
+ "agency",
+ "direct",
+ "other",
+ "custom"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/server/database/migrations/meta/0050_snapshot.json b/server/database/migrations/meta/0050_snapshot.json
new file mode 100644
index 00000000..464045ca
--- /dev/null
+++ b/server/database/migrations/meta/0050_snapshot.json
@@ -0,0 +1,7849 @@
+{
+ "id": "36f987fc-91b3-4096-b47a-cc242da5b1f9",
+ "prevId": "987f8813-63f4-4dfa-8118-1355299d1847",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "account_user_id_idx": {
+ "name": "account_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invitation": {
+ "name": "invitation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "inviter_id": {
+ "name": "inviter_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "invitation_organization_id_idx": {
+ "name": "invitation_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invitation_email_idx": {
+ "name": "invitation_email_idx",
+ "columns": [
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invitation_inviter_id_user_id_fk": {
+ "name": "invitation_inviter_id_user_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "user",
+ "columnsFrom": [
+ "inviter_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invitation_organization_id_organization_id_fk": {
+ "name": "invitation_organization_id_organization_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.member": {
+ "name": "member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'member'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "member_user_id_idx": {
+ "name": "member_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "member_organization_id_idx": {
+ "name": "member_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "member_user_org_unique_idx": {
+ "name": "member_user_org_unique_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "member_user_id_user_id_fk": {
+ "name": "member_user_id_user_id_fk",
+ "tableFrom": "member",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "member_organization_id_organization_id_fk": {
+ "name": "member_organization_id_organization_id_fk",
+ "tableFrom": "member",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization": {
+ "name": "organization",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_slug_unique": {
+ "name": "organization_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.rate_limit": {
+ "name": "rate_limit",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "count": {
+ "name": "count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_request": {
+ "name": "last_request",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "rate_limit_key_idx": {
+ "name": "rate_limit_key_idx",
+ "columns": [
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "session_user_id_idx": {
+ "name": "session_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.subscription": {
+ "name": "subscription",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "plan": {
+ "name": "plan",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reference_id": {
+ "name": "reference_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_subscription_id": {
+ "name": "stripe_subscription_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'incomplete'"
+ },
+ "period_start": {
+ "name": "period_start",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "period_end": {
+ "name": "period_end",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trial_start": {
+ "name": "trial_start",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trial_end": {
+ "name": "trial_end",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancel_at_period_end": {
+ "name": "cancel_at_period_end",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "cancel_at": {
+ "name": "cancel_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "canceled_at": {
+ "name": "canceled_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ended_at": {
+ "name": "ended_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "seats": {
+ "name": "seats",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "billing_interval": {
+ "name": "billing_interval",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_schedule_id": {
+ "name": "stripe_schedule_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "subscription_reference_id_idx": {
+ "name": "subscription_reference_id_idx",
+ "columns": [
+ {
+ "expression": "reference_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "subscription_stripe_customer_id_idx": {
+ "name": "subscription_stripe_customer_id_idx",
+ "columns": [
+ {
+ "expression": "stripe_customer_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "verification_identifier_idx": {
+ "name": "verification_identifier_idx",
+ "columns": [
+ {
+ "expression": "identifier",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.activity_log": {
+ "name": "activity_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "activity_action",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_type": {
+ "name": "resource_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_id": {
+ "name": "resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "activity_log_organization_id_idx": {
+ "name": "activity_log_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_actor_id_idx": {
+ "name": "activity_log_actor_id_idx",
+ "columns": [
+ {
+ "expression": "actor_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_resource_idx": {
+ "name": "activity_log_resource_idx",
+ "columns": [
+ {
+ "expression": "resource_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "resource_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_created_at_idx": {
+ "name": "activity_log_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "activity_log_organization_id_organization_id_fk": {
+ "name": "activity_log_organization_id_organization_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "activity_log_actor_id_user_id_fk": {
+ "name": "activity_log_actor_id_user_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "user",
+ "columnsFrom": [
+ "actor_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ai_config": {
+ "name": "ai_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Default'"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openai'"
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'gpt-4o-mini'"
+ },
+ "api_key_encrypted": {
+ "name": "api_key_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 4096
+ },
+ "input_price_per_1m": {
+ "name": "input_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output_price_per_1m": {
+ "name": "output_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default_chatbot": {
+ "name": "is_default_chatbot",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_default_analysis": {
+ "name": "is_default_analysis",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ai_config_organization_id_idx": {
+ "name": "ai_config_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ai_config_default_chatbot_idx": {
+ "name": "ai_config_default_chatbot_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"ai_config\".\"is_default_chatbot\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ai_config_default_analysis_idx": {
+ "name": "ai_config_default_analysis_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"ai_config\".\"is_default_analysis\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ai_config_organization_id_organization_id_fk": {
+ "name": "ai_config_organization_id_organization_id_fk",
+ "tableFrom": "ai_config",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.analysis_run": {
+ "name": "analysis_run",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "analysis_run_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'completed'"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "criteria_snapshot": {
+ "name": "criteria_snapshot",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "composite_score": {
+ "name": "composite_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "prompt_tokens": {
+ "name": "prompt_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completion_tokens": {
+ "name": "completion_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cost_usd_micros": {
+ "name": "cost_usd_micros",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "billing_mode": {
+ "name": "billing_mode",
+ "type": "analysis_billing_mode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'byok'"
+ },
+ "raw_response": {
+ "name": "raw_response",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scored_by_id": {
+ "name": "scored_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "analysis_run_organization_id_idx": {
+ "name": "analysis_run_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "analysis_run_application_id_idx": {
+ "name": "analysis_run_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "analysis_run_created_at_idx": {
+ "name": "analysis_run_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "analysis_run_organization_id_organization_id_fk": {
+ "name": "analysis_run_organization_id_organization_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "analysis_run_application_id_application_id_fk": {
+ "name": "analysis_run_application_id_application_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "analysis_run_scored_by_id_user_id_fk": {
+ "name": "analysis_run_scored_by_id_user_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "user",
+ "columnsFrom": [
+ "scored_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.application": {
+ "name": "application",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "application_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'new'"
+ },
+ "score": {
+ "name": "score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cover_letter_text": {
+ "name": "cover_letter_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "application_organization_id_idx": {
+ "name": "application_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_candidate_id_idx": {
+ "name": "application_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_job_id_idx": {
+ "name": "application_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_org_candidate_job_idx": {
+ "name": "application_org_candidate_job_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "application_organization_id_organization_id_fk": {
+ "name": "application_organization_id_organization_id_fk",
+ "tableFrom": "application",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_candidate_id_candidate_id_fk": {
+ "name": "application_candidate_id_candidate_id_fk",
+ "tableFrom": "application",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_job_id_job_id_fk": {
+ "name": "application_job_id_job_id_fk",
+ "tableFrom": "application",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.application_source": {
+ "name": "application_source",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "source_channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'direct'"
+ },
+ "tracking_link_id": {
+ "name": "tracking_link_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_source": {
+ "name": "utm_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_medium": {
+ "name": "utm_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_campaign": {
+ "name": "utm_campaign",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_term": {
+ "name": "utm_term",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_content": {
+ "name": "utm_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "referrer_domain": {
+ "name": "referrer_domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "application_source_organization_id_idx": {
+ "name": "application_source_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_application_id_idx": {
+ "name": "application_source_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_channel_idx": {
+ "name": "application_source_channel_idx",
+ "columns": [
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_tracking_link_id_idx": {
+ "name": "application_source_tracking_link_id_idx",
+ "columns": [
+ {
+ "expression": "tracking_link_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_application_idx": {
+ "name": "application_source_application_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "application_source_organization_id_organization_id_fk": {
+ "name": "application_source_organization_id_organization_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_source_application_id_application_id_fk": {
+ "name": "application_source_application_id_application_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_source_tracking_link_id_tracking_link_id_fk": {
+ "name": "application_source_tracking_link_id_tracking_link_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "tracking_link",
+ "columnsFrom": [
+ "tracking_link_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.calendar_integration": {
+ "name": "calendar_integration",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "calendar_provider",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'google'"
+ },
+ "access_token_encrypted": {
+ "name": "access_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token_encrypted": {
+ "name": "refresh_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "calendar_id": {
+ "name": "calendar_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'primary'"
+ },
+ "account_email": {
+ "name": "account_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_channel_id": {
+ "name": "webhook_channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_resource_id": {
+ "name": "webhook_resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_expiration": {
+ "name": "webhook_expiration",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_token": {
+ "name": "sync_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "calendar_integration_user_provider_idx": {
+ "name": "calendar_integration_user_provider_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "calendar_integration_webhook_channel_idx": {
+ "name": "calendar_integration_webhook_channel_idx",
+ "columns": [
+ {
+ "expression": "webhook_channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "calendar_integration_user_id_user_id_fk": {
+ "name": "calendar_integration_user_id_user_id_fk",
+ "tableFrom": "calendar_integration",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate": {
+ "name": "candidate",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone": {
+ "name": "phone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gender": {
+ "name": "gender",
+ "type": "gender",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "date_of_birth": {
+ "name": "date_of_birth",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "quick_notes": {
+ "name": "quick_notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "retention_exempt_until": {
+ "name": "retention_exempt_until",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "retention_exempt_reason": {
+ "name": "retention_exempt_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "retention_reviewed_at": {
+ "name": "retention_reviewed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "quarantined_at": {
+ "name": "quarantined_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scheduled_purge_at": {
+ "name": "scheduled_purge_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_organization_id_idx": {
+ "name": "candidate_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_gender_idx": {
+ "name": "candidate_gender_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "gender",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_org_email_idx": {
+ "name": "candidate_org_email_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_quarantine_idx": {
+ "name": "candidate_quarantine_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scheduled_purge_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_organization_id_organization_id_fk": {
+ "name": "candidate_organization_id_organization_id_fk",
+ "tableFrom": "candidate",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_conversation": {
+ "name": "candidate_conversation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reply_token": {
+ "name": "reply_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "unread_count": {
+ "name": "unread_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_message_at": {
+ "name": "last_message_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_conversation_organization_id_idx": {
+ "name": "candidate_conversation_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_conversation_application_id_idx": {
+ "name": "candidate_conversation_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_conversation_reply_token_idx": {
+ "name": "candidate_conversation_reply_token_idx",
+ "columns": [
+ {
+ "expression": "reply_token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_conversation_last_message_at_idx": {
+ "name": "candidate_conversation_last_message_at_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "last_message_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_conversation_organization_id_organization_id_fk": {
+ "name": "candidate_conversation_organization_id_organization_id_fk",
+ "tableFrom": "candidate_conversation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_conversation_application_id_application_id_fk": {
+ "name": "candidate_conversation_application_id_application_id_fk",
+ "tableFrom": "candidate_conversation",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_message": {
+ "name": "candidate_message",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "conversation_id": {
+ "name": "conversation_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "direction": {
+ "name": "direction",
+ "type": "candidate_message_direction",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "candidate_message_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "from_email": {
+ "name": "from_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "to_email": {
+ "name": "to_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_text": {
+ "name": "body_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'message'"
+ },
+ "interview_id": {
+ "name": "interview_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "calendar_attachment_status": {
+ "name": "calendar_attachment_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'not_applicable'"
+ },
+ "calendar_attachment_error": {
+ "name": "calendar_attachment_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "calendar_sequence": {
+ "name": "calendar_sequence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "internet_message_id": {
+ "name": "internet_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "in_reply_to": {
+ "name": "in_reply_to",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "references": {
+ "name": "references",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_by_id": {
+ "name": "sent_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_status_at": {
+ "name": "provider_status_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_at": {
+ "name": "sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "delivered_at": {
+ "name": "delivered_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failed_at": {
+ "name": "failed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_code": {
+ "name": "error_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_message_organization_id_idx": {
+ "name": "candidate_message_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_conversation_id_idx": {
+ "name": "candidate_message_conversation_id_idx",
+ "columns": [
+ {
+ "expression": "conversation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_interview_id_idx": {
+ "name": "candidate_message_interview_id_idx",
+ "columns": [
+ {
+ "expression": "interview_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_provider_id_idx": {
+ "name": "candidate_message_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_internet_id_idx": {
+ "name": "candidate_message_internet_id_idx",
+ "columns": [
+ {
+ "expression": "internet_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_message_organization_id_organization_id_fk": {
+ "name": "candidate_message_organization_id_organization_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_message_conversation_id_candidate_conversation_id_fk": {
+ "name": "candidate_message_conversation_id_candidate_conversation_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "candidate_conversation",
+ "columnsFrom": [
+ "conversation_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_message_interview_id_interview_id_fk": {
+ "name": "candidate_message_interview_id_interview_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "interview",
+ "columnsFrom": [
+ "interview_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "candidate_message_sent_by_id_user_id_fk": {
+ "name": "candidate_message_sent_by_id_user_id_fk",
+ "tableFrom": "candidate_message",
+ "tableTo": "user",
+ "columnsFrom": [
+ "sent_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_message_attachment": {
+ "name": "candidate_message_attachment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message_id": {
+ "name": "message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "storage_key": {
+ "name": "storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_attachment_id": {
+ "name": "provider_attachment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_message_attachment_organization_id_idx": {
+ "name": "candidate_message_attachment_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_attachment_message_id_idx": {
+ "name": "candidate_message_attachment_message_id_idx",
+ "columns": [
+ {
+ "expression": "message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_message_attachment_organization_id_organization_id_fk": {
+ "name": "candidate_message_attachment_organization_id_organization_id_fk",
+ "tableFrom": "candidate_message_attachment",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "candidate_message_attachment_message_id_candidate_message_id_fk": {
+ "name": "candidate_message_attachment_message_id_candidate_message_id_fk",
+ "tableFrom": "candidate_message_attachment",
+ "tableTo": "candidate_message",
+ "columnsFrom": [
+ "message_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "candidate_message_attachment_storage_key_unique": {
+ "name": "candidate_message_attachment_storage_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "storage_key"
+ ]
+ },
+ "candidate_message_attachment_provider_attachment_id_unique": {
+ "name": "candidate_message_attachment_provider_attachment_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_attachment_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate_message_webhook_event": {
+ "name": "candidate_message_webhook_event",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "occurred_at": {
+ "name": "occurred_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "processed_at": {
+ "name": "processed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_message_webhook_provider_id_idx": {
+ "name": "candidate_message_webhook_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_message_webhook_unprocessed_idx": {
+ "name": "candidate_message_webhook_unprocessed_idx",
+ "columns": [
+ {
+ "expression": "processed_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.career_page": {
+ "name": "career_page",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "accent_color": {
+ "name": "accent_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "headline": {
+ "name": "headline",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "logo_storage_key": {
+ "name": "logo_storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "banner_storage_key": {
+ "name": "banner_storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "banner_position": {
+ "name": "banner_position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "career_page_organization_id_idx": {
+ "name": "career_page_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "career_page_slug_idx": {
+ "name": "career_page_slug_idx",
+ "columns": [
+ {
+ "expression": "slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "career_page_organization_id_organization_id_fk": {
+ "name": "career_page_organization_id_organization_id_fk",
+ "tableFrom": "career_page",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_agent": {
+ "name": "chatbot_agent",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "system_prompt": {
+ "name": "system_prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "temperature": {
+ "name": "temperature",
+ "type": "numeric(3, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_agent_org_user_idx": {
+ "name": "chatbot_agent_org_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "chatbot_agent_default_per_user_idx": {
+ "name": "chatbot_agent_default_per_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"chatbot_agent\".\"is_default\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_agent_organization_id_organization_id_fk": {
+ "name": "chatbot_agent_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_agent",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_agent_user_id_user_id_fk": {
+ "name": "chatbot_agent_user_id_user_id_fk",
+ "tableFrom": "chatbot_agent",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_conversation": {
+ "name": "chatbot_conversation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "folder_id": {
+ "name": "folder_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ai_config_id": {
+ "name": "ai_config_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'New chat'"
+ },
+ "scope": {
+ "name": "scope",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thinking": {
+ "name": "thinking",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "pinned": {
+ "name": "pinned",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "last_message_preview": {
+ "name": "last_message_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message_at": {
+ "name": "last_message_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_conversation_org_user_idx": {
+ "name": "chatbot_conversation_org_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "chatbot_conversation_folder_idx": {
+ "name": "chatbot_conversation_folder_idx",
+ "columns": [
+ {
+ "expression": "folder_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "chatbot_conversation_last_message_at_idx": {
+ "name": "chatbot_conversation_last_message_at_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "last_message_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_conversation_organization_id_organization_id_fk": {
+ "name": "chatbot_conversation_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_user_id_user_id_fk": {
+ "name": "chatbot_conversation_user_id_user_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_folder_id_chatbot_folder_id_fk": {
+ "name": "chatbot_conversation_folder_id_chatbot_folder_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "chatbot_folder",
+ "columnsFrom": [
+ "folder_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_agent_id_chatbot_agent_id_fk": {
+ "name": "chatbot_conversation_agent_id_chatbot_agent_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "chatbot_agent",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "chatbot_conversation_ai_config_id_ai_config_id_fk": {
+ "name": "chatbot_conversation_ai_config_id_ai_config_id_fk",
+ "tableFrom": "chatbot_conversation",
+ "tableTo": "ai_config",
+ "columnsFrom": [
+ "ai_config_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_folder": {
+ "name": "chatbot_folder",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "icon": {
+ "name": "icon",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "position": {
+ "name": "position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_folder_org_user_idx": {
+ "name": "chatbot_folder_org_user_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_folder_organization_id_organization_id_fk": {
+ "name": "chatbot_folder_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_folder",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_folder_user_id_user_id_fk": {
+ "name": "chatbot_folder_user_id_user_id_fk",
+ "tableFrom": "chatbot_folder",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.chatbot_message": {
+ "name": "chatbot_message",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "conversation_id": {
+ "name": "conversation_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "chatbot_message_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "reasoning": {
+ "name": "reasoning",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tool_calls": {
+ "name": "tool_calls",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sources": {
+ "name": "sources",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attachments": {
+ "name": "attachments",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "chatbot_message_conversation_idx": {
+ "name": "chatbot_message_conversation_idx",
+ "columns": [
+ {
+ "expression": "conversation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "chatbot_message_conversation_id_chatbot_conversation_id_fk": {
+ "name": "chatbot_message_conversation_id_chatbot_conversation_id_fk",
+ "tableFrom": "chatbot_message",
+ "tableTo": "chatbot_conversation",
+ "columnsFrom": [
+ "conversation_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_message_organization_id_organization_id_fk": {
+ "name": "chatbot_message_organization_id_organization_id_fk",
+ "tableFrom": "chatbot_message",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "chatbot_message_user_id_user_id_fk": {
+ "name": "chatbot_message_user_id_user_id_fk",
+ "tableFrom": "chatbot_message",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.comment": {
+ "name": "comment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_id": {
+ "name": "author_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "comment_target",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "comment_organization_id_idx": {
+ "name": "comment_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_target_idx": {
+ "name": "comment_target_idx",
+ "columns": [
+ {
+ "expression": "target_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_author_id_idx": {
+ "name": "comment_author_id_idx",
+ "columns": [
+ {
+ "expression": "author_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "comment_organization_id_organization_id_fk": {
+ "name": "comment_organization_id_organization_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_author_id_user_id_fk": {
+ "name": "comment_author_id_user_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "user",
+ "columnsFrom": [
+ "author_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.criterion_score": {
+ "name": "criterion_score",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "criterion_key": {
+ "name": "criterion_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "max_score": {
+ "name": "max_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "applicant_score": {
+ "name": "applicant_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "confidence": {
+ "name": "confidence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "evidence": {
+ "name": "evidence",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "strengths": {
+ "name": "strengths",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gaps": {
+ "name": "gaps",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "criterion_score_organization_id_idx": {
+ "name": "criterion_score_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "criterion_score_application_id_idx": {
+ "name": "criterion_score_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "criterion_score_app_criterion_idx": {
+ "name": "criterion_score_app_criterion_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "criterion_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "criterion_score_organization_id_organization_id_fk": {
+ "name": "criterion_score_organization_id_organization_id_fk",
+ "tableFrom": "criterion_score",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "criterion_score_application_id_application_id_fk": {
+ "name": "criterion_score_application_id_application_id_fk",
+ "tableFrom": "criterion_score",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.document": {
+ "name": "document",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "document_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'resume'"
+ },
+ "storage_key": {
+ "name": "storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "original_filename": {
+ "name": "original_filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "parsed_content": {
+ "name": "parsed_content",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "document_organization_id_idx": {
+ "name": "document_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "document_candidate_id_idx": {
+ "name": "document_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "document_organization_id_organization_id_fk": {
+ "name": "document_organization_id_organization_id_fk",
+ "tableFrom": "document",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "document_candidate_id_candidate_id_fk": {
+ "name": "document_candidate_id_candidate_id_fk",
+ "tableFrom": "document",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "document_storage_key_unique": {
+ "name": "document_storage_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "storage_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_suppression": {
+ "name": "email_suppression",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "email_suppression_reason",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_suppression_email_idx": {
+ "name": "email_suppression_email_idx",
+ "columns": [
+ {
+ "expression": "lower(\"email\")",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_template": {
+ "name": "email_template",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_template_organization_id_idx": {
+ "name": "email_template_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "email_template_created_by_id_idx": {
+ "name": "email_template_created_by_id_idx",
+ "columns": [
+ {
+ "expression": "created_by_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_template_organization_id_organization_id_fk": {
+ "name": "email_template_organization_id_organization_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "email_template_created_by_id_user_id_fk": {
+ "name": "email_template_created_by_id_user_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.import_job": {
+ "name": "import_job",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "import_source",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "import_job_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'processing'"
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mapping": {
+ "name": "mapping",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "target_job_id": {
+ "name": "target_job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_rows": {
+ "name": "total_rows",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "committed_rows": {
+ "name": "committed_rows",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "import_job_organization_id_idx": {
+ "name": "import_job_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "import_job_status_idx": {
+ "name": "import_job_status_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "import_job_organization_id_organization_id_fk": {
+ "name": "import_job_organization_id_organization_id_fk",
+ "tableFrom": "import_job",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_job_created_by_user_id_fk": {
+ "name": "import_job_created_by_user_id_fk",
+ "tableFrom": "import_job",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_job_target_job_id_job_id_fk": {
+ "name": "import_job_target_job_id_job_id_fk",
+ "tableFrom": "import_job",
+ "tableTo": "job",
+ "columnsFrom": [
+ "target_job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.import_row": {
+ "name": "import_row",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "row_index": {
+ "name": "row_index",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "raw_data": {
+ "name": "raw_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "normalized_data": {
+ "name": "normalized_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "import_row_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'ready'"
+ },
+ "dedupe_hash": {
+ "name": "dedupe_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "matched_candidate_id": {
+ "name": "matched_candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_candidate_id": {
+ "name": "created_candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "import_row_job_id_idx": {
+ "name": "import_row_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "import_row_job_status_idx": {
+ "name": "import_row_job_status_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "import_row_dedupe_idx": {
+ "name": "import_row_dedupe_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "dedupe_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "import_row_organization_id_organization_id_fk": {
+ "name": "import_row_organization_id_organization_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_row_job_id_import_job_id_fk": {
+ "name": "import_row_job_id_import_job_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "import_job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "import_row_matched_candidate_id_candidate_id_fk": {
+ "name": "import_row_matched_candidate_id_candidate_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "matched_candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "import_row_created_candidate_id_candidate_id_fk": {
+ "name": "import_row_created_candidate_id_candidate_id_fk",
+ "tableFrom": "import_row",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "created_candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.interview": {
+ "name": "interview",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "interview_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'video'"
+ },
+ "status": {
+ "name": "status",
+ "type": "interview_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'scheduled'"
+ },
+ "scheduled_at": {
+ "name": "scheduled_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "duration": {
+ "name": "duration",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 60
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "personal_note": {
+ "name": "personal_note",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "interviewers": {
+ "name": "interviewers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "invitation_sent_at": {
+ "name": "invitation_sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "candidate_response": {
+ "name": "candidate_response",
+ "type": "candidate_response",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "candidate_responded_at": {
+ "name": "candidate_responded_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_calendar_event_id": {
+ "name": "google_calendar_event_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_calendar_event_link": {
+ "name": "google_calendar_event_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'UTC'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "interview_organization_id_idx": {
+ "name": "interview_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_application_id_idx": {
+ "name": "interview_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_scheduled_at_idx": {
+ "name": "interview_scheduled_at_idx",
+ "columns": [
+ {
+ "expression": "scheduled_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_status_idx": {
+ "name": "interview_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_created_by_id_idx": {
+ "name": "interview_created_by_id_idx",
+ "columns": [
+ {
+ "expression": "created_by_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "interview_organization_id_organization_id_fk": {
+ "name": "interview_organization_id_organization_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "interview_application_id_application_id_fk": {
+ "name": "interview_application_id_application_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "interview_created_by_id_user_id_fk": {
+ "name": "interview_created_by_id_user_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invite_link": {
+ "name": "invite_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'member'"
+ },
+ "max_uses": {
+ "name": "max_uses",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "use_count": {
+ "name": "use_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "invite_link_organization_id_idx": {
+ "name": "invite_link_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invite_link_token_idx": {
+ "name": "invite_link_token_idx",
+ "columns": [
+ {
+ "expression": "token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invite_link_organization_id_organization_id_fk": {
+ "name": "invite_link_organization_id_organization_id_fk",
+ "tableFrom": "invite_link",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invite_link_created_by_id_user_id_fk": {
+ "name": "invite_link_created_by_id_user_id_fk",
+ "tableFrom": "invite_link",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "invite_link_token_unique": {
+ "name": "invite_link_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job": {
+ "name": "job",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "job_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'full_time'"
+ },
+ "status": {
+ "name": "status",
+ "type": "job_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "salary_min": {
+ "name": "salary_min",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_max": {
+ "name": "salary_max",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_currency": {
+ "name": "salary_currency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_unit": {
+ "name": "salary_unit",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_negotiable": {
+ "name": "salary_negotiable",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "remote_status": {
+ "name": "remote_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "valid_through": {
+ "name": "valid_through",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "experience_level": {
+ "name": "experience_level",
+ "type": "experience_level",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "phone_requirement": {
+ "name": "phone_requirement",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'optional'"
+ },
+ "require_resume": {
+ "name": "require_resume",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "require_cover_letter": {
+ "name": "require_cover_letter",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "auto_score_on_apply": {
+ "name": "auto_score_on_apply",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "analysis_context": {
+ "name": "analysis_context",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{\"coverLetter\":true,\"screeningAnswers\":true,\"recruiterNotes\":false}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "job_organization_id_idx": {
+ "name": "job_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_organization_id_organization_id_fk": {
+ "name": "job_organization_id_organization_id_fk",
+ "tableFrom": "job",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "job_slug_unique": {
+ "name": "job_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job_question": {
+ "name": "job_question",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "question_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'short_text'"
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "required": {
+ "name": "required",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "options": {
+ "name": "options",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "job_question_organization_id_idx": {
+ "name": "job_question_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "job_question_job_id_idx": {
+ "name": "job_question_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_question_organization_id_organization_id_fk": {
+ "name": "job_question_organization_id_organization_id_fk",
+ "tableFrom": "job_question",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "job_question_job_id_job_id_fk": {
+ "name": "job_question_job_id_job_id_fk",
+ "tableFrom": "job_question",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.join_request": {
+ "name": "join_request",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "join_request_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "reviewed_by_id": {
+ "name": "reviewed_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reviewed_at": {
+ "name": "reviewed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "join_request_organization_id_idx": {
+ "name": "join_request_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "join_request_user_id_idx": {
+ "name": "join_request_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "join_request_status_idx": {
+ "name": "join_request_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "join_request_user_id_user_id_fk": {
+ "name": "join_request_user_id_user_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "join_request_organization_id_organization_id_fk": {
+ "name": "join_request_organization_id_organization_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "join_request_reviewed_by_id_user_id_fk": {
+ "name": "join_request_reviewed_by_id_user_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "user",
+ "columnsFrom": [
+ "reviewed_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification_outbox": {
+ "name": "notification_outbox",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipient_user_id": {
+ "name": "recipient_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipient_email": {
+ "name": "recipient_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "notification_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cadence": {
+ "name": "cadence",
+ "type": "notification_cadence",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "notification_outbox_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "attempts": {
+ "name": "attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_attempt_at": {
+ "name": "next_attempt_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "digest_bucket": {
+ "name": "digest_bucket",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_at": {
+ "name": "sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failed_at": {
+ "name": "failed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "notification_outbox_dedupe_key_idx": {
+ "name": "notification_outbox_dedupe_key_idx",
+ "columns": [
+ {
+ "expression": "dedupe_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_pull_idx": {
+ "name": "notification_outbox_pull_idx",
+ "columns": [
+ {
+ "expression": "cadence",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_attempt_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"notification_outbox\".\"status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_provider_id_idx": {
+ "name": "notification_outbox_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"notification_outbox\".\"provider_message_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_organization_id_idx": {
+ "name": "notification_outbox_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_outbox_digest_idx": {
+ "name": "notification_outbox_digest_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "recipient_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "digest_bucket",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "notification_outbox_organization_id_organization_id_fk": {
+ "name": "notification_outbox_organization_id_organization_id_fk",
+ "tableFrom": "notification_outbox",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_outbox_recipient_user_id_user_id_fk": {
+ "name": "notification_outbox_recipient_user_id_user_id_fk",
+ "tableFrom": "notification_outbox",
+ "tableTo": "user",
+ "columnsFrom": [
+ "recipient_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification_preference": {
+ "name": "notification_preference",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "notification_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_mode": {
+ "name": "channel_mode",
+ "type": "notification_channel_mode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "notification_preference_user_org_type_idx": {
+ "name": "notification_preference_user_org_type_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notification_preference_organization_id_idx": {
+ "name": "notification_preference_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "notification_preference_user_id_user_id_fk": {
+ "name": "notification_preference_user_id_user_id_fk",
+ "tableFrom": "notification_preference",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notification_preference_organization_id_organization_id_fk": {
+ "name": "notification_preference_organization_id_organization_id_fk",
+ "tableFrom": "notification_preference",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.onboarding_survey_response": {
+ "name": "onboarding_survey_response",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "signup_plan": {
+ "name": "signup_plan",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "signup_billing": {
+ "name": "signup_billing",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "company_size": {
+ "name": "company_size",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_role": {
+ "name": "user_role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "discovery_source": {
+ "name": "discovery_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "current_hiring_process": {
+ "name": "current_hiring_process",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expected_roles_12m": {
+ "name": "expected_roles_12m",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "answered_count": {
+ "name": "answered_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "skipped_count": {
+ "name": "skipped_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "onboarding_survey_response_user_id_idx": {
+ "name": "onboarding_survey_response_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "onboarding_survey_response_organization_id_idx": {
+ "name": "onboarding_survey_response_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "onboarding_survey_response_user_id_user_id_fk": {
+ "name": "onboarding_survey_response_user_id_user_id_fk",
+ "tableFrom": "onboarding_survey_response",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "onboarding_survey_response_organization_id_organization_id_fk": {
+ "name": "onboarding_survey_response_organization_id_organization_id_fk",
+ "tableFrom": "onboarding_survey_response",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.org_settings": {
+ "name": "org_settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name_display_format": {
+ "name": "name_display_format",
+ "type": "name_display_format",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'first_last'"
+ },
+ "date_format": {
+ "name": "date_format",
+ "type": "date_format",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'mdy'"
+ },
+ "retention_enabled": {
+ "name": "retention_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "retention_months": {
+ "name": "retention_months",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 24
+ },
+ "quarantine_days": {
+ "name": "quarantine_days",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 30
+ },
+ "retention_activated_at": {
+ "name": "retention_activated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "privacy_policy_url": {
+ "name": "privacy_policy_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "privacy_policy_text": {
+ "name": "privacy_policy_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "privacy_contact_email": {
+ "name": "privacy_contact_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "org_settings_organization_id_idx": {
+ "name": "org_settings_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "org_settings_organization_id_organization_id_fk": {
+ "name": "org_settings_organization_id_organization_id_fk",
+ "tableFrom": "org_settings",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.platform_ai_config": {
+ "name": "platform_ai_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'Platform (OpenRouter)'"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openrouter'"
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openai/gpt-5.4-mini'"
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 4096
+ },
+ "input_price_per_1m": {
+ "name": "input_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output_price_per_1m": {
+ "name": "output_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default_analysis": {
+ "name": "is_default_analysis",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "is_enabled": {
+ "name": "is_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "platform_ai_config_organization_id_idx": {
+ "name": "platform_ai_config_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "platform_ai_config_organization_id_organization_id_fk": {
+ "name": "platform_ai_config_organization_id_organization_id_fk",
+ "tableFrom": "platform_ai_config",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.property_definition": {
+ "name": "property_definition",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "property_entity_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "property_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "property_definition_org_idx": {
+ "name": "property_definition_org_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_definition_org_entity_idx": {
+ "name": "property_definition_org_entity_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_definition_job_idx": {
+ "name": "property_definition_job_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "property_definition_organization_id_organization_id_fk": {
+ "name": "property_definition_organization_id_organization_id_fk",
+ "tableFrom": "property_definition",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "property_definition_job_id_job_id_fk": {
+ "name": "property_definition_job_id_job_id_fk",
+ "tableFrom": "property_definition",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.property_value": {
+ "name": "property_value",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "property_definition_id": {
+ "name": "property_definition_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "property_entity_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entity_id": {
+ "name": "entity_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "property_value_org_idx": {
+ "name": "property_value_org_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_value_entity_idx": {
+ "name": "property_value_entity_idx",
+ "columns": [
+ {
+ "expression": "entity_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_value_definition_idx": {
+ "name": "property_value_definition_idx",
+ "columns": [
+ {
+ "expression": "property_definition_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "property_value_def_entity_idx": {
+ "name": "property_value_def_entity_idx",
+ "columns": [
+ {
+ "expression": "property_definition_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "property_value_organization_id_organization_id_fk": {
+ "name": "property_value_organization_id_organization_id_fk",
+ "tableFrom": "property_value",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "property_value_property_definition_id_property_definition_id_fk": {
+ "name": "property_value_property_definition_id_property_definition_id_fk",
+ "tableFrom": "property_value",
+ "tableTo": "property_definition",
+ "columnsFrom": [
+ "property_definition_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.question_response": {
+ "name": "question_response",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "question_id": {
+ "name": "question_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "question_response_organization_id_idx": {
+ "name": "question_response_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "question_response_application_id_idx": {
+ "name": "question_response_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "question_response_question_id_idx": {
+ "name": "question_response_question_id_idx",
+ "columns": [
+ {
+ "expression": "question_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "question_response_organization_id_organization_id_fk": {
+ "name": "question_response_organization_id_organization_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "question_response_application_id_application_id_fk": {
+ "name": "question_response_application_id_application_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "question_response_question_id_job_question_id_fk": {
+ "name": "question_response_question_id_job_question_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "job_question",
+ "columnsFrom": [
+ "question_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.retention_audit": {
+ "name": "retention_audit",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "retention_audit_action",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "result": {
+ "name": "result",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'success'"
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "retention_audit_organization_id_idx": {
+ "name": "retention_audit_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "retention_audit_candidate_id_idx": {
+ "name": "retention_audit_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "retention_audit_created_at_idx": {
+ "name": "retention_audit_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "retention_audit_organization_id_organization_id_fk": {
+ "name": "retention_audit_organization_id_organization_id_fk",
+ "tableFrom": "retention_audit",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scoring_criterion": {
+ "name": "scoring_criterion",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "category": {
+ "name": "category",
+ "type": "criterion_category",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'custom'"
+ },
+ "max_score": {
+ "name": "max_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 10
+ },
+ "weight": {
+ "name": "weight",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scoring_criterion_organization_id_idx": {
+ "name": "scoring_criterion_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scoring_criterion_job_id_idx": {
+ "name": "scoring_criterion_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scoring_criterion_job_key_idx": {
+ "name": "scoring_criterion_job_key_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scoring_criterion_organization_id_organization_id_fk": {
+ "name": "scoring_criterion_organization_id_organization_id_fk",
+ "tableFrom": "scoring_criterion",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "scoring_criterion_job_id_job_id_fk": {
+ "name": "scoring_criterion_job_id_job_id_fk",
+ "tableFrom": "scoring_criterion",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tracking_link": {
+ "name": "tracking_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "channel": {
+ "name": "channel",
+ "type": "source_channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'custom'"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code": {
+ "name": "code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "utm_source": {
+ "name": "utm_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_medium": {
+ "name": "utm_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_campaign": {
+ "name": "utm_campaign",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_term": {
+ "name": "utm_term",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_content": {
+ "name": "utm_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "click_count": {
+ "name": "click_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "application_count": {
+ "name": "application_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "tracking_link_organization_id_idx": {
+ "name": "tracking_link_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_job_id_idx": {
+ "name": "tracking_link_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_code_idx": {
+ "name": "tracking_link_code_idx",
+ "columns": [
+ {
+ "expression": "code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_channel_idx": {
+ "name": "tracking_link_channel_idx",
+ "columns": [
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tracking_link_organization_id_organization_id_fk": {
+ "name": "tracking_link_organization_id_organization_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tracking_link_job_id_job_id_fk": {
+ "name": "tracking_link_job_id_job_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tracking_link_created_by_id_user_id_fk": {
+ "name": "tracking_link_created_by_id_user_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "tracking_link_code_unique": {
+ "name": "tracking_link_code_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "code"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sso_provider": {
+ "name": "sso_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oidc_config": {
+ "name": "oidc_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "saml_config": {
+ "name": "saml_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "sso_provider_domain_idx": {
+ "name": "sso_provider_domain_idx",
+ "columns": [
+ {
+ "expression": "domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "sso_provider_provider_id_idx": {
+ "name": "sso_provider_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "sso_provider_organization_id_idx": {
+ "name": "sso_provider_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "sso_provider_user_id_user_id_fk": {
+ "name": "sso_provider_user_id_user_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "sso_provider_organization_id_organization_id_fk": {
+ "name": "sso_provider_organization_id_organization_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.activity_action": {
+ "name": "activity_action",
+ "schema": "public",
+ "values": [
+ "created",
+ "updated",
+ "deleted",
+ "status_changed",
+ "comment_added",
+ "member_invited",
+ "member_removed",
+ "member_role_changed",
+ "scored"
+ ]
+ },
+ "public.analysis_billing_mode": {
+ "name": "analysis_billing_mode",
+ "schema": "public",
+ "values": [
+ "platform",
+ "byok"
+ ]
+ },
+ "public.analysis_run_status": {
+ "name": "analysis_run_status",
+ "schema": "public",
+ "values": [
+ "completed",
+ "failed",
+ "partial"
+ ]
+ },
+ "public.application_status": {
+ "name": "application_status",
+ "schema": "public",
+ "values": [
+ "new",
+ "screening",
+ "interview",
+ "offer",
+ "hired",
+ "rejected"
+ ]
+ },
+ "public.calendar_provider": {
+ "name": "calendar_provider",
+ "schema": "public",
+ "values": [
+ "google"
+ ]
+ },
+ "public.candidate_message_direction": {
+ "name": "candidate_message_direction",
+ "schema": "public",
+ "values": [
+ "inbound",
+ "outbound"
+ ]
+ },
+ "public.candidate_message_status": {
+ "name": "candidate_message_status",
+ "schema": "public",
+ "values": [
+ "queued",
+ "sent",
+ "delivered",
+ "delayed",
+ "bounced",
+ "failed",
+ "complained"
+ ]
+ },
+ "public.candidate_response": {
+ "name": "candidate_response",
+ "schema": "public",
+ "values": [
+ "pending",
+ "accepted",
+ "declined",
+ "tentative"
+ ]
+ },
+ "public.chatbot_message_role": {
+ "name": "chatbot_message_role",
+ "schema": "public",
+ "values": [
+ "user",
+ "assistant"
+ ]
+ },
+ "public.comment_target": {
+ "name": "comment_target",
+ "schema": "public",
+ "values": [
+ "candidate",
+ "application",
+ "job"
+ ]
+ },
+ "public.criterion_category": {
+ "name": "criterion_category",
+ "schema": "public",
+ "values": [
+ "technical",
+ "experience",
+ "soft_skills",
+ "education",
+ "culture",
+ "custom"
+ ]
+ },
+ "public.date_format": {
+ "name": "date_format",
+ "schema": "public",
+ "values": [
+ "mdy",
+ "dmy",
+ "ymd"
+ ]
+ },
+ "public.document_type": {
+ "name": "document_type",
+ "schema": "public",
+ "values": [
+ "resume",
+ "cover_letter",
+ "other"
+ ]
+ },
+ "public.email_suppression_reason": {
+ "name": "email_suppression_reason",
+ "schema": "public",
+ "values": [
+ "bounce",
+ "complaint"
+ ]
+ },
+ "public.experience_level": {
+ "name": "experience_level",
+ "schema": "public",
+ "values": [
+ "junior",
+ "mid",
+ "senior",
+ "lead"
+ ]
+ },
+ "public.gender": {
+ "name": "gender",
+ "schema": "public",
+ "values": [
+ "male",
+ "female",
+ "other",
+ "prefer_not_to_say"
+ ]
+ },
+ "public.import_job_status": {
+ "name": "import_job_status",
+ "schema": "public",
+ "values": [
+ "processing",
+ "previewing",
+ "committing",
+ "completed",
+ "failed"
+ ]
+ },
+ "public.import_row_status": {
+ "name": "import_row_status",
+ "schema": "public",
+ "values": [
+ "ready",
+ "duplicate",
+ "duplicate_in_file",
+ "error",
+ "committed",
+ "skipped"
+ ]
+ },
+ "public.import_source": {
+ "name": "import_source",
+ "schema": "public",
+ "values": [
+ "csv",
+ "xlsx",
+ "resume_zip"
+ ]
+ },
+ "public.interview_status": {
+ "name": "interview_status",
+ "schema": "public",
+ "values": [
+ "scheduled",
+ "completed",
+ "cancelled",
+ "no_show"
+ ]
+ },
+ "public.interview_type": {
+ "name": "interview_type",
+ "schema": "public",
+ "values": [
+ "phone",
+ "video",
+ "in_person",
+ "panel",
+ "technical",
+ "take_home"
+ ]
+ },
+ "public.job_status": {
+ "name": "job_status",
+ "schema": "public",
+ "values": [
+ "draft",
+ "open",
+ "closed",
+ "archived"
+ ]
+ },
+ "public.job_type": {
+ "name": "job_type",
+ "schema": "public",
+ "values": [
+ "full_time",
+ "part_time",
+ "contract",
+ "internship"
+ ]
+ },
+ "public.join_request_status": {
+ "name": "join_request_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "approved",
+ "rejected"
+ ]
+ },
+ "public.name_display_format": {
+ "name": "name_display_format",
+ "schema": "public",
+ "values": [
+ "first_last",
+ "last_first"
+ ]
+ },
+ "public.notification_cadence": {
+ "name": "notification_cadence",
+ "schema": "public",
+ "values": [
+ "instant",
+ "digest"
+ ]
+ },
+ "public.notification_channel_mode": {
+ "name": "notification_channel_mode",
+ "schema": "public",
+ "values": [
+ "instant",
+ "digest",
+ "off"
+ ]
+ },
+ "public.notification_outbox_status": {
+ "name": "notification_outbox_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "sent",
+ "skipped",
+ "dead"
+ ]
+ },
+ "public.notification_type": {
+ "name": "notification_type",
+ "schema": "public",
+ "values": [
+ "candidate_replied",
+ "application_created",
+ "interview_response"
+ ]
+ },
+ "public.property_entity_type": {
+ "name": "property_entity_type",
+ "schema": "public",
+ "values": [
+ "candidate",
+ "application"
+ ]
+ },
+ "public.property_type": {
+ "name": "property_type",
+ "schema": "public",
+ "values": [
+ "text",
+ "long_text",
+ "number",
+ "select",
+ "multi_select",
+ "date",
+ "checkbox",
+ "url",
+ "email",
+ "person",
+ "file"
+ ]
+ },
+ "public.question_type": {
+ "name": "question_type",
+ "schema": "public",
+ "values": [
+ "short_text",
+ "long_text",
+ "single_select",
+ "multi_select",
+ "number",
+ "date",
+ "url",
+ "checkbox",
+ "file_upload"
+ ]
+ },
+ "public.retention_audit_action": {
+ "name": "retention_audit_action",
+ "schema": "public",
+ "values": [
+ "quarantined",
+ "restored",
+ "erased",
+ "exempted",
+ "unexempted",
+ "exported"
+ ]
+ },
+ "public.source_channel": {
+ "name": "source_channel",
+ "schema": "public",
+ "values": [
+ "linkedin",
+ "indeed",
+ "glassdoor",
+ "ziprecruiter",
+ "monster",
+ "handshake",
+ "angellist",
+ "wellfound",
+ "dice",
+ "stackoverflow",
+ "weworkremotely",
+ "remoteok",
+ "builtin",
+ "hired",
+ "lever",
+ "greenhouse_board",
+ "google_jobs",
+ "facebook",
+ "twitter",
+ "instagram",
+ "tiktok",
+ "reddit",
+ "referral",
+ "career_site",
+ "email",
+ "event",
+ "agency",
+ "direct",
+ "other",
+ "custom"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/server/database/migrations/meta/_journal.json b/server/database/migrations/meta/_journal.json
index 2edce7f3..9cc65ec4 100644
--- a/server/database/migrations/meta/_journal.json
+++ b/server/database/migrations/meta/_journal.json
@@ -337,6 +337,27 @@
"when": 1784465724426,
"tag": "0047_marvelous_sunfire",
"breakpoints": true
+ },
+ {
+ "idx": 48,
+ "version": "7",
+ "when": 1784533409757,
+ "tag": "0048_brave_human_torch",
+ "breakpoints": true
+ },
+ {
+ "idx": 49,
+ "version": "7",
+ "when": 1784536310607,
+ "tag": "0049_friendly_blacklash",
+ "breakpoints": true
+ },
+ {
+ "idx": 50,
+ "version": "7",
+ "when": 1784538415712,
+ "tag": "0050_numerous_red_wolf",
+ "breakpoints": true
}
]
-}
+}
\ No newline at end of file
diff --git a/server/database/schema/app.ts b/server/database/schema/app.ts
index 13536c79..2019c23f 100644
--- a/server/database/schema/app.ts
+++ b/server/database/schema/app.ts
@@ -946,6 +946,104 @@ export const analysisRun = pgTable('analysis_run', {
index('analysis_run_created_at_idx').on(t.createdAt),
]))
+// ─────────────────────────────────────────────
+// Recruiter Notifications (outbox + preferences + suppression)
+// ─────────────────────────────────────────────
+//
+// Reliable, long-term recruiter notification engine. Domain events are written
+// to `notificationOutbox` (ideally in the same tx as the triggering write so an
+// event is never lost), and a scheduled worker drains it, sends via Resend, and
+// retries with backoff. See server/utils/notifications/.
+
+/** Recruiter-facing events. New producers plug into the same engine. */
+export const notificationTypeEnum = pgEnum('notification_type', [
+ 'candidate_replied', 'application_created', 'interview_response',
+])
+/** How a single outbox row is delivered — immediately, or rolled into a daily digest. */
+export const notificationCadenceEnum = pgEnum('notification_cadence', ['instant', 'digest'])
+/** Per-recipient, per-type choice. `off` suppresses the event entirely for that user. */
+export const notificationChannelModeEnum = pgEnum('notification_channel_mode', ['instant', 'digest', 'off'])
+/** Outbox lifecycle: pending → sent | skipped | dead (past max retries). */
+export const notificationOutboxStatusEnum = pgEnum('notification_outbox_status', [
+ 'pending', 'sent', 'skipped', 'dead',
+])
+/** Why an address was suppressed — a hard bounce or a spam complaint. */
+export const emailSuppressionReasonEnum = pgEnum('email_suppression_reason', ['bounce', 'complaint'])
+
+/**
+ * Durable, at-least-once notification queue. One row per recipient per event.
+ *
+ * `dedupeKey` is `:` so re-enqueuing the same
+ * event is a no-op (unique index + onConflictDoNothing), while distinct recipients
+ * each get their own row. The worker pulls `status='pending'` rows whose
+ * `nextAttemptAt` has elapsed; `providerMessageId` links Resend delivery webhooks
+ * back to the row.
+ */
+export const notificationOutbox = pgTable('notification_outbox', {
+ id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
+ organizationId: text('organization_id').notNull().references(() => organization.id, { onDelete: 'cascade' }),
+ recipientUserId: text('recipient_user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
+ recipientEmail: text('recipient_email').notNull(),
+ type: notificationTypeEnum('type').notNull(),
+ cadence: notificationCadenceEnum('cadence').notNull(),
+ /** Idempotency key: `:`. Globally unique. */
+ dedupeKey: text('dedupe_key').notNull(),
+ /** Rendering context for the template (candidate name, job title, deep-link ids, …). */
+ payload: jsonb('payload').$type>().notNull(),
+ status: notificationOutboxStatusEnum('status').notNull().default('pending'),
+ attempts: integer('attempts').notNull().default(0),
+ nextAttemptAt: timestamp('next_attempt_at').notNull().defaultNow(),
+ /** Digest grouping key (YYYY-MM-DD, recipient timezone-agnostic v1). NULL for instant rows. */
+ digestBucket: text('digest_bucket'),
+ /** Resend message id — set on send, used by the delivery webhook to update status. */
+ providerMessageId: text('provider_message_id'),
+ sentAt: timestamp('sent_at'),
+ failedAt: timestamp('failed_at'),
+ lastError: text('last_error'),
+ createdAt: timestamp('created_at').notNull().defaultNow(),
+ updatedAt: timestamp('updated_at').notNull().defaultNow(),
+}, (t) => ([
+ uniqueIndex('notification_outbox_dedupe_key_idx').on(t.dedupeKey),
+ // Drives cadence-specific pending-row pulls without indexing terminal rows.
+ index('notification_outbox_pull_idx').on(t.cadence, t.nextAttemptAt)
+ .where(sql`${t.status} = 'pending'`),
+ // Webhook lookup by provider message id (partial: only rows that have been sent).
+ uniqueIndex('notification_outbox_provider_id_idx').on(t.providerMessageId).where(sql`${t.providerMessageId} is not null`),
+ index('notification_outbox_organization_id_idx').on(t.organizationId),
+ index('notification_outbox_digest_idx').on(t.organizationId, t.recipientUserId, t.digestBucket),
+]))
+
+/**
+ * Per-recipient, per-type delivery preference. Absent row = the sensible default
+ * resolved in code (candidate_replied/interview_response -> instant,
+ * application_created -> digest). See server/utils/notifications/recipients.ts.
+ */
+export const notificationPreference = pgTable('notification_preference', {
+ id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
+ userId: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
+ organizationId: text('organization_id').notNull().references(() => organization.id, { onDelete: 'cascade' }),
+ type: notificationTypeEnum('type').notNull(),
+ channelMode: notificationChannelModeEnum('channel_mode').notNull(),
+ createdAt: timestamp('created_at').notNull().defaultNow(),
+ updatedAt: timestamp('updated_at').notNull().defaultNow(),
+}, (t) => ([
+ uniqueIndex('notification_preference_user_org_type_idx').on(t.userId, t.organizationId, t.type),
+ index('notification_preference_organization_id_idx').on(t.organizationId),
+]))
+
+/**
+ * Addresses we must never send to again. Fed by hard-bounce / complaint webhooks
+ * and checked at recipient resolution — protects long-term sender reputation.
+ */
+export const emailSuppression = pgTable('email_suppression', {
+ id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
+ email: text('email').notNull(),
+ reason: emailSuppressionReasonEnum('reason').notNull(),
+ createdAt: timestamp('created_at').notNull().defaultNow(),
+}, (t) => ([
+ uniqueIndex('email_suppression_email_idx').on(sql`lower(${t.email})`),
+]))
+
// ─────────────────────────────────────────────
// Relations
// ─────────────────────────────────────────────
diff --git a/server/scripts/seed.ts b/server/scripts/seed.ts
index 93d51c34..77a1c6b1 100644
--- a/server/scripts/seed.ts
+++ b/server/scripts/seed.ts
@@ -57,6 +57,11 @@ const DEMO_EMAIL = "demo@reqcore.com";
const DEMO_PASSWORD = process.env.DEMO_PASSWORD ?? "demo1234";
const DEMO_ORG_NAME = "Reqcore Demo";
const DEMO_ORG_SLUG = "reqcore-demo";
+const DEMO_NOTIFICATION_TYPES = [
+ "candidate_replied",
+ "application_created",
+ "interview_response",
+] as const;
// Legacy values from the old applirank.com domain — cleaned up on seed
const LEGACY_DEMO_EMAIL = "demo@applirank.com";
@@ -120,6 +125,37 @@ function generateSlug(title: string, uuid: string): string {
return `${base}-${shortId}`;
}
+async function disableDemoEmailNotifications(
+ userId: string,
+ organizationId: string,
+): Promise {
+ const now = new Date();
+
+ for (const type of DEMO_NOTIFICATION_TYPES) {
+ await db
+ .insert(schema.notificationPreference)
+ .values({
+ id: id(),
+ userId,
+ organizationId,
+ type,
+ channelMode: "off",
+ createdAt: now,
+ updatedAt: now,
+ })
+ .onConflictDoUpdate({
+ target: [
+ schema.notificationPreference.userId,
+ schema.notificationPreference.organizationId,
+ schema.notificationPreference.type,
+ ],
+ set: { channelMode: "off", updatedAt: now },
+ });
+ }
+
+ console.log("✅ Disabled email notifications for the demo account");
+}
+
// ─────────────────────────────────────────────
// Seed Data Definitions
// ─────────────────────────────────────────────
@@ -9345,6 +9381,8 @@ async function seed() {
console.log("✅ Linked demo user to existing org as owner");
}
+ await disableDemoEmailNotifications(userId, existingOrg.id);
+
console.log("⚠️ Demo organization already exists. Skipping full seed.");
console.log(
" To re-seed all data, delete the organization first or reset the database.",
@@ -9372,6 +9410,8 @@ async function seed() {
createdAt: daysAgo(30),
});
+ await disableDemoEmailNotifications(userId, orgId);
+
console.log(`✅ Created organization: ${DEMO_ORG_NAME}`);
// 3. Create jobs
diff --git a/server/tasks/notification-digest.ts b/server/tasks/notification-digest.ts
new file mode 100644
index 00000000..80493874
--- /dev/null
+++ b/server/tasks/notification-digest.ts
@@ -0,0 +1,13 @@
+import { defineTask } from 'nitropack/runtime/task'
+import { runNotificationDigest } from '../utils/notifications/dispatch'
+
+export default defineTask({
+ meta: {
+ name: 'notification-digest',
+ description: 'Group pending digest-cadence notifications into one daily summary email per recipient',
+ },
+ async run() {
+ const result = await runNotificationDigest({ source: 'scheduled_task' })
+ return { result }
+ },
+})
diff --git a/server/tasks/notification-dispatch.ts b/server/tasks/notification-dispatch.ts
new file mode 100644
index 00000000..54908820
--- /dev/null
+++ b/server/tasks/notification-dispatch.ts
@@ -0,0 +1,13 @@
+import { defineTask } from 'nitropack/runtime/task'
+import { runNotificationDispatch } from '../utils/notifications/dispatch'
+
+export default defineTask({
+ meta: {
+ name: 'notification-dispatch',
+ description: 'Drain the recruiter notification outbox and send instant emails via Resend',
+ },
+ async run() {
+ const result = await runNotificationDispatch({ source: 'scheduled_task' })
+ return { result }
+ },
+})
diff --git a/server/utils/ai/chatTools.ts b/server/utils/ai/chatTools.ts
index 8310da15..9ea8b5d0 100644
--- a/server/utils/ai/chatTools.ts
+++ b/server/utils/ai/chatTools.ts
@@ -12,7 +12,7 @@
* `list_*` to discover IDs, then `get_*` to fetch details.
*/
import { tool } from 'ai'
-import { and, desc, eq, ilike, inArray, or } from 'drizzle-orm'
+import { and, desc, eq, ilike, inArray, isNull, or } from 'drizzle-orm'
import { z } from 'zod'
import {
application,
@@ -62,6 +62,11 @@ function truncate(text: string, max: number): string {
}
export function buildChatbotTools(ctx: ChatbotToolContext) {
+ const activeCandidateIds = db.select({ id: candidate.id }).from(candidate).where(and(
+ eq(candidate.organizationId, ctx.orgId),
+ isNull(candidate.quarantinedAt),
+ ))
+
return {
list_jobs: tool({
description:
@@ -157,6 +162,7 @@ export function buildChatbotTools(ctx: ChatbotToolContext) {
const conditions = [
eq(application.organizationId, ctx.orgId),
eq(application.jobId, jobId),
+ inArray(application.candidateId, activeCandidateIds),
]
if (status) conditions.push(eq(application.status, status))
@@ -195,6 +201,7 @@ export function buildChatbotTools(ctx: ChatbotToolContext) {
const rows = await db.query.candidate.findMany({
where: and(
eq(candidate.organizationId, ctx.orgId),
+ isNull(candidate.quarantinedAt),
or(
ilike(candidate.firstName, like),
ilike(candidate.lastName, like),
@@ -250,6 +257,7 @@ export function buildChatbotTools(ctx: ChatbotToolContext) {
where: and(
eq(candidate.organizationId, ctx.orgId),
eq(candidate.id, candidateId),
+ isNull(candidate.quarantinedAt),
),
})
if (!c) throw new Error(`Candidate ${candidateId} not found.`)
@@ -345,6 +353,7 @@ export function buildChatbotTools(ctx: ChatbotToolContext) {
where: and(
eq(document.organizationId, ctx.orgId),
eq(document.id, documentId),
+ inArray(document.candidateId, activeCandidateIds),
),
})
if (!doc) throw new Error(`Document ${documentId} not found.`)
@@ -398,6 +407,7 @@ export function buildChatbotTools(ctx: ChatbotToolContext) {
where: and(
eq(application.organizationId, ctx.orgId),
eq(application.id, applicationId),
+ inArray(application.candidateId, activeCandidateIds),
),
columns: { id: true, score: true, jobId: true },
})
diff --git a/server/utils/email.ts b/server/utils/email.ts
index 1a2f96e0..342d86ee 100644
--- a/server/utils/email.ts
+++ b/server/utils/email.ts
@@ -1,6 +1,8 @@
import { Resend } from 'resend'
import nodemailer from 'nodemailer'
import type { Transporter } from 'nodemailer'
+import { escapeHtml, renderNotificationLayout } from './notifications/templates'
+import type { NotificationType } from '~~/shared/notifications'
// ─── Resend client ────────────────────────────────────────────────────────────
@@ -60,19 +62,27 @@ interface EmailMessage {
icsAttachment?: Buffer
/** Resend-only metadata tags — silently ignored by SMTP. */
resendTags?: Array<{ name: string; value: string }>
+ /** Resend-only idempotency key — dedupes retried sends at the provider. */
+ idempotencyKey?: string
/** Message logged to console when no provider is configured (dev fallback). */
logFallback: string
/** logError category used on transport failure. */
errorCategory: string
}
+/** Outcome of a transport send. `providerMessageId` is Resend-only (null for SMTP/console). */
+export interface SendEmailResult {
+ providerMessageId: string | null
+}
+
/**
* Route an outbound email through SMTP (preferred) → Resend → console fallback.
* Priority: SMTP_HOST set → use SMTP. Else RESEND_API_KEY set → use Resend.
* Otherwise logs the fallback message and returns (no error thrown).
* Throws on transport errors so callers can decide whether to swallow them.
+ * Returns the Resend message id when available so callers can track delivery.
*/
-async function sendEmail(msg: EmailMessage): Promise {
+async function sendEmail(msg: EmailMessage): Promise {
const from = getFromEmail()
// 1. SMTP — takes priority when SMTP_HOST is configured
@@ -97,7 +107,7 @@ async function sendEmail(msg: EmailMessage): Promise {
})
throw err
}
- return
+ return { providerMessageId: null }
}
// 2. Resend
@@ -107,7 +117,7 @@ async function sendEmail(msg: EmailMessage): Promise {
? [{ filename: 'interview.ics', content: msg.icsAttachment.toString('base64'), content_type: 'text/calendar; method=REQUEST' }]
: undefined
- const { error } = await resend.emails.send({
+ const { data, error } = await resend.emails.send({
from,
to: [msg.to],
subject: msg.subject,
@@ -115,7 +125,7 @@ async function sendEmail(msg: EmailMessage): Promise {
text: msg.text,
...(resendAttachments ? { attachments: resendAttachments } : {}),
...(msg.resendTags ? { tags: msg.resendTags } : {}),
- })
+ }, msg.idempotencyKey ? { idempotencyKey: msg.idempotencyKey } : undefined)
if (error) {
logError(msg.errorCategory, {
@@ -124,11 +134,45 @@ async function sendEmail(msg: EmailMessage): Promise {
})
throw new Error(error.message)
}
- return
+ return { providerMessageId: data?.id ?? null }
}
// 3. No provider configured — dev/test fallback
console.info(`[Reqcore] ${msg.logFallback}`)
+ return { providerMessageId: null }
+}
+
+/**
+ * Send a recruiter notification email. Routes through the shared `sendEmail`
+ * transport (SMTP → Resend → console), tagging it `category: notification` so
+ * the Resend delivery webhook can map events back to the outbox, and passing the
+ * outbox row id as the Resend idempotency key to make retried sends safe.
+ * Returns the provider message id for the worker to persist.
+ */
+export async function sendNotificationEmail(msg: {
+ to: string
+ subject: string
+ html: string
+ text: string
+ organizationId: string
+ /** Resend `type` tag — an event type, or `digest` for the daily roll-up. */
+ type: NotificationType | 'digest'
+ idempotencyKey: string
+}): Promise {
+ return sendEmail({
+ to: msg.to,
+ subject: msg.subject,
+ html: msg.html,
+ text: msg.text,
+ idempotencyKey: msg.idempotencyKey,
+ resendTags: [
+ { name: 'category', value: 'notification' },
+ { name: 'organization', value: msg.organizationId },
+ { name: 'type', value: msg.type },
+ ],
+ logFallback: `Notification email (${msg.type}) → ${msg.to} | ${msg.subject}`,
+ errorCategory: 'email.notification_send_failed',
+ })
}
export interface CandidateMessageEmailResult {
@@ -328,65 +372,18 @@ function buildInvitationHtml(params: {
}): string {
const { inviterName, organizationName, role, inviteLink } = params
- return `
-
-
-
-
- You're invited to ${escapeHtml(organizationName)}
-
-
-
-
-
-
-
-
-
- Reqcore
-
-
-
-
-
- You've been invited
-
- ${escapeHtml(inviterName)} has invited you to join
- ${escapeHtml(organizationName)} as a ${escapeHtml(role)} .
-
-
- Click the button below to accept the invitation. You'll need to sign in or create an account first.
-
-
-
-
- This invitation expires in 48 hours. If you didn't expect this email, you can safely ignore it.
-
-
-
-
-
-
-
- Sent by Reqcore — Open-source applicant tracking
-
-
-
-
-
-
-
-
-`
+ return renderNotificationLayout({
+ title: `You're invited to ${organizationName}`,
+ heading: "You've been invited",
+ bodyHtml:
+ ``
+ + `${escapeHtml(inviterName)} has invited you to join `
+ + `${escapeHtml(organizationName)} as a ${escapeHtml(role)} .
`
+ + ``
+ + `Click the button below to accept the invitation. You'll need to sign in or create an account first.
`,
+ cta: { label: 'Accept Invitation', url: inviteLink },
+ note: "This invitation expires in 48 hours. If you didn't expect this email, you can safely ignore it.",
+ })
}
function buildInvitationText(params: {
@@ -410,72 +407,20 @@ function buildInvitationText(params: {
].join('\n')
}
-/**
- * Escape HTML special characters to prevent XSS in email templates.
- */
-function escapeHtml(str: string): string {
- return str
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
-}
-
// ─────────────────────────────────────────────
// Email verification & password reset templates
// ─────────────────────────────────────────────
function buildVerificationHtml(params: { url: string }): string {
- return `
-
-
-
-
- Verify your email
-
-
-
-
-
-
-
-
- Reqcore
-
-
-
-
- Verify your email
-
- Click the button below to verify your email address and activate your account.
-
-
-
- If you didn't create an account, you can safely ignore this email.
-
-
-
-
-
- Sent by Reqcore — Open-source applicant tracking
-
-
-
-
-
-
-
-`
+ return renderNotificationLayout({
+ title: 'Verify your email',
+ heading: 'Verify your email',
+ bodyHtml:
+ ``
+ + `Click the button below to verify your email address and activate your account.
`,
+ cta: { label: 'Verify Email', url: params.url },
+ note: "If you didn't create an account, you can safely ignore this email.",
+ })
}
function buildVerificationText(params: { url: string }): string {
@@ -492,55 +437,15 @@ function buildVerificationText(params: { url: string }): string {
}
function buildPasswordResetHtml(params: { url: string }): string {
- return `
-
-
-
-
- Reset your password
-
-
-
-
-
-
-
-
- Reqcore
-
-
-
-
- Reset your password
-
- Click the button below to reset your password. This link will expire shortly.
-
-
-
- If you didn't request a password reset, you can safely ignore this email.
-
-
-
-
-
- Sent by Reqcore — Open-source applicant tracking
-
-
-
-
-
-
-
-`
+ return renderNotificationLayout({
+ title: 'Reset your password',
+ heading: 'Reset your password',
+ bodyHtml:
+ ``
+ + `Click the button below to reset your password. This link will expire shortly.
`,
+ cta: { label: 'Reset Password', url: params.url },
+ note: "If you didn't request a password reset, you can safely ignore this email.",
+ })
}
function buildPasswordResetText(params: { url: string }): string {
diff --git a/server/utils/env.ts b/server/utils/env.ts
index d8a00390..99f0bdef 100644
--- a/server/utils/env.ts
+++ b/server/utils/env.ts
@@ -176,6 +176,16 @@ export const envSchema = z
val => val === undefined || val === '' ? false : val === true || val === 'true',
z.boolean().default(false),
),
+ /**
+ * Kill-switch for the recruiter notification engine. Fail-safe default = ON:
+ * notifications are an activation driver and are safe to send. Set to false to
+ * pause all outbox draining/digests instance-wide (checked at the top of the
+ * dispatch + digest workers). Mirrors the GDPR_CLEANUP_ENABLED idiom.
+ */
+ NOTIFICATIONS_ENABLED: z.preprocess(
+ val => val === undefined || val === '' ? true : val === true || val === 'true',
+ z.boolean().default(true),
+ ),
// ── Platform AI gateway (optional) ──────────────────────────
// When OPENROUTER_API_KEY is set, orgs without their own AI config fall back
// to our platform key, routed through OpenRouter for unified billing and
diff --git a/server/utils/notifications/delivery-status.ts b/server/utils/notifications/delivery-status.ts
new file mode 100644
index 00000000..9e87f890
--- /dev/null
+++ b/server/utils/notifications/delivery-status.ts
@@ -0,0 +1,82 @@
+import type { WebhookEventPayload } from 'resend'
+import { and, eq } from 'drizzle-orm'
+import { emailSuppression, notificationOutbox } from '../../database/schema'
+
+const NOTIFICATION_STATUS_EVENTS = [
+ 'email.sent',
+ 'email.delivered',
+ 'email.delivery_delayed',
+ 'email.bounced',
+ 'email.failed',
+ 'email.complained',
+] as const
+
+export type NotificationStatusEvent = Extract<
+ WebhookEventPayload,
+ { type: (typeof NOTIFICATION_STATUS_EVENTS)[number] }
+>
+
+function statusError(event: NotificationStatusEvent): { code: string, message: string } | null {
+ if (event.type === 'email.bounced') {
+ return { code: event.data.bounce.type, message: event.data.bounce.message }
+ }
+ if (event.type === 'email.failed') {
+ return { code: 'failed', message: event.data.failed.reason }
+ }
+ if (event.type === 'email.complained') {
+ return { code: 'complained', message: 'The recipient marked this message as spam' }
+ }
+ return null
+}
+
+function suppressionForEvent(event: NotificationStatusEvent): 'bounce' | 'complaint' | null {
+ if (event.type === 'email.complained') return 'complaint'
+ if (event.type === 'email.bounced') {
+ const type = event.data.bounce.type?.toLowerCase() ?? ''
+ if (type.includes('hard') || type.includes('permanent')) return 'bounce'
+ }
+ return null
+}
+
+export async function processNotificationStatusEvent(event: NotificationStatusEvent): Promise {
+ const eventAt = new Date(event.created_at)
+ const outboxRow = await db.query.notificationOutbox.findFirst({
+ where: eq(notificationOutbox.providerMessageId, event.data.email_id),
+ columns: {
+ id: true,
+ organizationId: true,
+ recipientUserId: true,
+ recipientEmail: true,
+ cadence: true,
+ digestBucket: true,
+ },
+ })
+
+ if (!outboxRow) return
+
+ const identity = outboxRow.cadence === 'digest' && outboxRow.digestBucket
+ ? and(
+ eq(notificationOutbox.organizationId, outboxRow.organizationId),
+ eq(notificationOutbox.recipientUserId, outboxRow.recipientUserId),
+ eq(notificationOutbox.recipientEmail, outboxRow.recipientEmail),
+ eq(notificationOutbox.digestBucket, outboxRow.digestBucket),
+ )
+ : eq(notificationOutbox.id, outboxRow.id)
+
+ const isFailure = event.type === 'email.bounced'
+ || event.type === 'email.failed'
+ || event.type === 'email.complained'
+ const error = statusError(event)
+ await db.update(notificationOutbox).set({
+ ...(isFailure ? { status: 'dead' as const, failedAt: eventAt } : {}),
+ ...(error ? { lastError: `${error.code}: ${error.message}`.slice(0, 1000) } : {}),
+ updatedAt: new Date(),
+ }).where(identity)
+
+ const suppression = suppressionForEvent(event)
+ if (suppression) {
+ await db.insert(emailSuppression)
+ .values({ email: outboxRow.recipientEmail.trim().toLowerCase(), reason: suppression })
+ .onConflictDoNothing()
+ }
+}
diff --git a/server/utils/notifications/dispatch.ts b/server/utils/notifications/dispatch.ts
new file mode 100644
index 00000000..0953bef3
--- /dev/null
+++ b/server/utils/notifications/dispatch.ts
@@ -0,0 +1,323 @@
+/** Durable notification outbox dispatch with leases, retries, and digest grouping. */
+import { and, asc, eq, gt, inArray, lte, sql } from 'drizzle-orm'
+import { emailSuppression, notificationOutbox } from '../../database/schema/app'
+import { isServerFeatureEnabled } from '../featureFlags'
+import { sendNotificationEmail } from '../email'
+import { renderDigest, renderNotification } from './templates'
+import { applicationUrl, dashboardUrl, inboxConversationUrl, interviewUrl } from './urls'
+import type { NotificationType } from '~~/shared/notifications'
+
+const MAX_ATTEMPTS = 6
+const DEFAULT_BATCH = 50
+const LEASE_MS = 10 * 60_000
+
+type OutboxRow = typeof notificationOutbox.$inferSelect
+type NotificationDbClient = typeof db | Parameters[0]>[0]
+
+export interface NotificationDispatchResult {
+ enabled: boolean
+ source: NotificationDispatchSource
+ processed: number
+ sent: number
+ skipped: number
+ retried: number
+ dead: number
+}
+
+export type NotificationDispatchSource = 'scheduled_task' | 'cron_endpoint' | 'interactive'
+
+function clampBatch(n: number | undefined): number {
+ return Math.min(500, Math.max(1, n ?? DEFAULT_BATCH))
+}
+
+function backoffMs(attempts: number): number {
+ return Math.min(60_000 * 2 ** Math.max(0, attempts - 1), 60 * 60_000)
+}
+
+function disabledResult(source: NotificationDispatchSource): NotificationDispatchResult {
+ return { enabled: false, source, processed: 0, sent: 0, skipped: 0, retried: 0, dead: 0 }
+}
+
+function emptyResult(source: NotificationDispatchSource): NotificationDispatchResult {
+ return { enabled: true, source, processed: 0, sent: 0, skipped: 0, retried: 0, dead: 0 }
+}
+
+function mergeResults(a: NotificationDispatchResult, b: NotificationDispatchResult): NotificationDispatchResult {
+ return {
+ enabled: a.enabled && b.enabled,
+ source: a.source,
+ processed: a.processed + b.processed,
+ sent: a.sent + b.sent,
+ skipped: a.skipped + b.skipped,
+ retried: a.retried + b.retried,
+ dead: a.dead + b.dead,
+ }
+}
+
+function rowApplicationUrl(row: OutboxRow): string {
+ const payload = row.payload as Record
+ if (row.type === 'interview_response') {
+ return interviewUrl(String(payload.interviewId ?? ''))
+ }
+ if (row.type === 'candidate_replied' && payload.conversationId) {
+ return inboxConversationUrl(String(payload.conversationId))
+ }
+ return applicationUrl(String(payload.applicationId ?? ''))
+}
+
+function enrichPayload(row: OutboxRow): Record {
+ const targetUrl = rowApplicationUrl(row)
+ return {
+ ...(row.payload as Record),
+ applicationUrl: targetUrl,
+ interviewUrl: targetUrl,
+ }
+}
+
+async function isSuppressed(email: string, dbc: NotificationDbClient = db): Promise {
+ const rows = await dbc.select({ id: emailSuppression.id })
+ .from(emailSuppression)
+ .where(eq(sql`lower(${emailSuppression.email})`, email.toLowerCase()))
+ .limit(1)
+ return rows.length > 0
+}
+
+async function markSent(id: string, providerMessageId: string | null): Promise {
+ await db.update(notificationOutbox)
+ .set({ status: 'sent', providerMessageId, sentAt: new Date(), lastError: null, updatedAt: new Date() })
+ .where(eq(notificationOutbox.id, id))
+}
+
+async function markSkipped(ids: string[], reason: string): Promise {
+ if (ids.length === 0) return
+ await db.update(notificationOutbox)
+ .set({ status: 'skipped', lastError: reason, updatedAt: new Date() })
+ .where(inArray(notificationOutbox.id, ids))
+}
+
+async function recordFailure(row: OutboxRow, err: unknown): Promise<'dead' | 'retried'> {
+ const attempts = row.attempts + 1
+ const message = (err instanceof Error ? err.message : String(err)).slice(0, 1000)
+ if (attempts >= MAX_ATTEMPTS) {
+ await db.update(notificationOutbox)
+ .set({ status: 'dead', attempts, lastError: message, failedAt: new Date(), updatedAt: new Date() })
+ .where(eq(notificationOutbox.id, row.id))
+ logError('notification.dispatch_dead', { outbox_id: row.id, type: row.type, attempts })
+ return 'dead'
+ }
+ await db.update(notificationOutbox)
+ .set({ attempts, nextAttemptAt: new Date(Date.now() + backoffMs(attempts)), lastError: message, updatedAt: new Date() })
+ .where(eq(notificationOutbox.id, row.id))
+ return 'retried'
+}
+
+async function claimInstantRows(batchSize: number, now: Date): Promise {
+ const candidates = await db.select({ id: notificationOutbox.id }).from(notificationOutbox)
+ .where(and(
+ eq(notificationOutbox.status, 'pending'),
+ eq(notificationOutbox.cadence, 'instant'),
+ lte(notificationOutbox.nextAttemptAt, now),
+ ))
+ .orderBy(asc(notificationOutbox.nextAttemptAt))
+ .limit(batchSize)
+
+ const claimed: OutboxRow[] = []
+ for (const candidate of candidates) {
+ const [row] = await db.update(notificationOutbox)
+ .set({ nextAttemptAt: new Date(now.getTime() + LEASE_MS), updatedAt: now })
+ .where(and(
+ eq(notificationOutbox.id, candidate.id),
+ eq(notificationOutbox.status, 'pending'),
+ lte(notificationOutbox.nextAttemptAt, now),
+ ))
+ .returning()
+ if (row) claimed.push(row)
+ }
+ return claimed
+}
+
+async function dispatchInstant(source: NotificationDispatchSource, batchSize: number): Promise {
+ const rows = await claimInstantRows(batchSize, new Date())
+ const result = emptyResult(source)
+ result.processed = rows.length
+
+ for (const row of rows) {
+ if (await isSuppressed(row.recipientEmail)) {
+ await markSkipped([row.id], 'email_suppressed')
+ result.skipped++
+ continue
+ }
+
+ const rendered = renderNotification(row.type as NotificationType, enrichPayload(row))
+ if (!rendered) {
+ await markSkipped([row.id], 'unrenderable_payload')
+ result.skipped++
+ continue
+ }
+ try {
+ const { providerMessageId } = await sendNotificationEmail({
+ to: row.recipientEmail,
+ subject: rendered.subject,
+ html: rendered.html,
+ text: rendered.text,
+ organizationId: row.organizationId,
+ type: row.type as NotificationType,
+ idempotencyKey: row.id,
+ })
+ await markSent(row.id, providerMessageId)
+ result.sent++
+ }
+ catch (err) {
+ const outcome = await recordFailure(row, err)
+ outcome === 'dead' ? result.dead++ : result.retried++
+ }
+ }
+ return result
+}
+
+interface DigestGroupKey {
+ organizationId: string
+ recipientUserId: string
+ recipientEmail: string
+ digestBucket: string | null
+}
+
+async function claimDigestGroup(key: DigestGroupKey, now: Date, retryOnly: boolean): Promise {
+ if (!key.digestBucket) return []
+ return db.update(notificationOutbox)
+ .set({ nextAttemptAt: new Date(now.getTime() + LEASE_MS), updatedAt: now })
+ .where(and(
+ eq(notificationOutbox.organizationId, key.organizationId),
+ eq(notificationOutbox.recipientUserId, key.recipientUserId),
+ eq(notificationOutbox.recipientEmail, key.recipientEmail),
+ eq(notificationOutbox.digestBucket, key.digestBucket),
+ eq(notificationOutbox.status, 'pending'),
+ eq(notificationOutbox.cadence, 'digest'),
+ lte(notificationOutbox.nextAttemptAt, now),
+ ...(retryOnly ? [gt(notificationOutbox.attempts, 0)] : []),
+ ))
+ .returning()
+}
+
+async function markDigestSent(rows: OutboxRow[], providerMessageId: string | null): Promise {
+ const ids = rows.map(row => row.id)
+ const leader = rows[0]
+ if (!leader) return
+ await db.transaction(async (tx) => {
+ await tx.update(notificationOutbox)
+ .set({ status: 'sent', providerMessageId: null, sentAt: new Date(), lastError: null, updatedAt: new Date() })
+ .where(inArray(notificationOutbox.id, ids))
+ if (providerMessageId) {
+ await tx.update(notificationOutbox)
+ .set({ providerMessageId })
+ .where(eq(notificationOutbox.id, leader.id))
+ }
+ })
+}
+
+async function dispatchDigest(
+ source: NotificationDispatchSource,
+ batchSize: number,
+ retryOnly: boolean,
+): Promise {
+ const now = new Date()
+ const groups = await db.select({
+ organizationId: notificationOutbox.organizationId,
+ recipientUserId: notificationOutbox.recipientUserId,
+ recipientEmail: notificationOutbox.recipientEmail,
+ digestBucket: notificationOutbox.digestBucket,
+ }).from(notificationOutbox)
+ .where(and(
+ eq(notificationOutbox.status, 'pending'),
+ eq(notificationOutbox.cadence, 'digest'),
+ lte(notificationOutbox.nextAttemptAt, now),
+ ...(retryOnly ? [gt(notificationOutbox.attempts, 0)] : []),
+ ))
+ .groupBy(
+ notificationOutbox.organizationId,
+ notificationOutbox.recipientUserId,
+ notificationOutbox.recipientEmail,
+ notificationOutbox.digestBucket,
+ )
+ .orderBy(asc(notificationOutbox.digestBucket))
+ .limit(batchSize)
+
+ const result = emptyResult(source)
+ for (const key of groups) {
+ const rows = await claimDigestGroup(key, now, retryOnly)
+ if (rows.length === 0 || !key.digestBucket) continue
+ result.processed += rows.length
+
+ if (await isSuppressed(key.recipientEmail)) {
+ await markSkipped(rows.map(row => row.id), 'email_suppressed')
+ result.skipped += rows.length
+ continue
+ }
+
+ const rendered = renderDigest({
+ items: rows.map(row => ({
+ type: row.type as NotificationType,
+ payload: row.payload as Record,
+ applicationUrl: rowApplicationUrl(row),
+ })),
+ dashboardUrl: dashboardUrl(),
+ })
+ if (!rendered) {
+ await markSkipped(rows.map(row => row.id), 'unrenderable_payload')
+ result.skipped += rows.length
+ continue
+ }
+ try {
+ const { providerMessageId } = await sendNotificationEmail({
+ to: key.recipientEmail,
+ subject: rendered.subject,
+ html: rendered.html,
+ text: rendered.text,
+ organizationId: key.organizationId,
+ type: 'digest',
+ idempotencyKey: `digest:${key.organizationId}:${key.recipientUserId}:${key.digestBucket}`,
+ })
+ await markDigestSent(rows, providerMessageId)
+ result.sent++
+ }
+ catch (err) {
+ result.retried++
+ for (const row of rows) {
+ const outcome = await recordFailure(row, err)
+ if (outcome === 'dead') result.dead++
+ }
+ }
+ }
+ return result
+}
+
+export async function runNotificationDispatch(
+ options: { source?: NotificationDispatchSource, batchSize?: number } = {},
+): Promise {
+ const source = options.source ?? 'scheduled_task'
+ if (!env.NOTIFICATIONS_ENABLED) {
+ logWarn('notification.dispatch_disabled', { source })
+ return disabledResult(source)
+ }
+
+ const batchSize = clampBatch(options.batchSize)
+ const instant = await dispatchInstant(source, batchSize)
+ const digestRetries = await dispatchDigest(source, batchSize, true)
+ const result = mergeResults(instant, digestRetries)
+ logInfo('notification.dispatch_completed', { ...result })
+ return result
+}
+
+export async function runNotificationDigest(
+ options: { source?: NotificationDispatchSource, batchSize?: number } = {},
+): Promise {
+ const source = options.source ?? 'scheduled_task'
+ if (!env.NOTIFICATIONS_ENABLED) {
+ logWarn('notification.digest_disabled', { source })
+ return disabledResult(source)
+ }
+
+ const result = await dispatchDigest(source, clampBatch(options.batchSize ?? 500), false)
+ logInfo('notification.digest_completed', { ...result })
+ return result
+}
diff --git a/server/utils/notifications/enqueue.ts b/server/utils/notifications/enqueue.ts
new file mode 100644
index 00000000..16c6a689
--- /dev/null
+++ b/server/utils/notifications/enqueue.ts
@@ -0,0 +1,66 @@
+/**
+ * Enqueue a recruiter notification event into the durable outbox.
+ *
+ * Modeled on `recordActivity` — it never throws, so a notification failure can
+ * never break the primary write. Producers must call it after their domain
+ * transaction commits; it deliberately uses the standalone database client so
+ * an outbox failure cannot poison or roll back that transaction.
+ *
+ * Idempotency: each recipient row's `dedupeKey` is `:`, and
+ * inserts use `onConflictDoNothing`, so re-running the same producer is a no-op.
+ */
+import { notificationOutbox } from '../../database/schema/app'
+import type { NotificationType } from '~~/shared/notifications'
+import { resolveRecipients } from './recipients'
+
+const DIGEST_HOUR_UTC = 8
+
+/** The next 08:00 UTC digest delivery date (YYYY-MM-DD). */
+export function digestBucketFor(date: Date = new Date()): string {
+ const cutoff = new Date(date)
+ cutoff.setUTCHours(DIGEST_HOUR_UTC, 0, 0, 0)
+ if (date >= cutoff) cutoff.setUTCDate(cutoff.getUTCDate() + 1)
+ return cutoff.toISOString().slice(0, 10)
+}
+
+export function digestDeliveryAt(bucket: string): Date {
+ return new Date(`${bucket}T${String(DIGEST_HOUR_UTC).padStart(2, '0')}:00:00.000Z`)
+}
+
+export async function enqueueNotification(params: {
+ organizationId: string
+ type: NotificationType
+ /** Event-scoped idempotency prefix, e.g. `application_created:`. */
+ dedupeKey: string
+ payload: Record
+}): Promise {
+ try {
+ const recipients = await resolveRecipients(params.organizationId, params.type)
+ if (recipients.length === 0) return
+
+ const bucket = digestBucketFor()
+ const rows = recipients.map(r => ({
+ organizationId: params.organizationId,
+ recipientUserId: r.userId,
+ recipientEmail: r.email,
+ type: params.type,
+ cadence: r.cadence,
+ dedupeKey: `${params.dedupeKey}:${r.userId}`,
+ payload: params.payload,
+ digestBucket: r.cadence === 'digest' ? bucket : null,
+ nextAttemptAt: r.cadence === 'digest' ? digestDeliveryAt(bucket) : new Date(),
+ }))
+
+ await db.insert(notificationOutbox).values(rows)
+ .onConflictDoNothing({ target: notificationOutbox.dedupeKey })
+ }
+ catch (err) {
+ // Enqueue must never break the primary operation.
+ logWarn('notification.enqueue_failed', {
+ organization_id: params.organizationId,
+ type: params.type,
+ dedupe_key: params.dedupeKey,
+ error_message: err instanceof Error ? err.message : String(err),
+ })
+ }
+}
diff --git a/server/utils/notifications/interview-response.ts b/server/utils/notifications/interview-response.ts
new file mode 100644
index 00000000..56298cbd
--- /dev/null
+++ b/server/utils/notifications/interview-response.ts
@@ -0,0 +1,26 @@
+import { enqueueNotification } from './enqueue'
+
+export type RecruiterInterviewResponse = 'accepted' | 'declined' | 'tentative'
+
+/** Enqueue one bounded recruiter alert for each distinct candidate RSVP state. */
+export async function enqueueInterviewResponseNotification(params: {
+ organizationId: string
+ interviewId: string
+ candidateName: string
+ jobTitle: string
+ interviewTitle: string
+ response: RecruiterInterviewResponse
+}): Promise {
+ await enqueueNotification({
+ organizationId: params.organizationId,
+ type: 'interview_response',
+ dedupeKey: `interview_response:${params.interviewId}:${params.response}`,
+ payload: {
+ interviewId: params.interviewId,
+ candidateName: params.candidateName,
+ jobTitle: params.jobTitle,
+ interviewTitle: params.interviewTitle,
+ response: params.response,
+ },
+ })
+}
diff --git a/server/utils/notifications/recipients.ts b/server/utils/notifications/recipients.ts
new file mode 100644
index 00000000..ed3a59c6
--- /dev/null
+++ b/server/utils/notifications/recipients.ts
@@ -0,0 +1,68 @@
+/**
+ * Resolve which org members should receive a given notification event, honoring
+ * per-recipient preferences and the email suppression list.
+ */
+import { and, eq, inArray, sql } from 'drizzle-orm'
+import { member, user } from '../../database/schema/auth'
+import { emailSuppression, notificationPreference } from '../../database/schema/app'
+import {
+ DEFAULT_CHANNEL_MODE,
+ normalizeNotificationChannelMode,
+ type NotificationCadence,
+ type NotificationType,
+} from '~~/shared/notifications'
+import { isDemoAccountEmail } from '../demoOrg'
+
+/** Either the global db or an open transaction — both expose the query/select API we use. */
+type Tx = Parameters[0]>[0]
+export type NotificationDbClient = typeof db | Tx
+
+export interface NotificationRecipient {
+ userId: string
+ email: string
+ cadence: NotificationCadence
+}
+
+/**
+ * Return the members of `organizationId` who want `type` delivered (mode !== 'off'
+ * after applying defaults), excluding any address on the suppression list.
+ */
+export async function resolveRecipients(
+ organizationId: string,
+ type: NotificationType,
+ dbc: NotificationDbClient = db,
+): Promise {
+ const members = await dbc
+ .select({ userId: member.userId, email: user.email })
+ .from(member)
+ .innerJoin(user, eq(member.userId, user.id))
+ .where(eq(member.organizationId, organizationId))
+
+ if (members.length === 0) return []
+
+ const prefs = await dbc
+ .select({ userId: notificationPreference.userId, channelMode: notificationPreference.channelMode })
+ .from(notificationPreference)
+ .where(and(
+ eq(notificationPreference.organizationId, organizationId),
+ eq(notificationPreference.type, type),
+ ))
+ const prefByUser = new Map(prefs.map(p => [p.userId, p.channelMode]))
+
+ const emails = members.map(m => m.email.toLowerCase())
+ const suppressed = await dbc
+ .select({ email: emailSuppression.email })
+ .from(emailSuppression)
+ .where(inArray(sql`lower(${emailSuppression.email})`, emails))
+ const suppressedSet = new Set(suppressed.map(s => s.email.toLowerCase()))
+
+ const recipients: NotificationRecipient[] = []
+ for (const m of members) {
+ if (isDemoAccountEmail(m.email)) continue
+ if (suppressedSet.has(m.email.toLowerCase())) continue
+ const mode = normalizeNotificationChannelMode(type, prefByUser.get(m.userId) ?? DEFAULT_CHANNEL_MODE[type])
+ if (mode === 'off') continue
+ recipients.push({ userId: m.userId, email: m.email, cadence: mode })
+ }
+ return recipients
+}
diff --git a/server/utils/notifications/templates.ts b/server/utils/notifications/templates.ts
new file mode 100644
index 00000000..46f5d477
--- /dev/null
+++ b/server/utils/notifications/templates.ts
@@ -0,0 +1,345 @@
+/**
+ * Recruiter notification email templates.
+ *
+ * `renderNotificationLayout` is the single shared HTML shell (header + card +
+ * footer) that the auth/invitation emails in `email.ts` also reuse — extracting
+ * it here pays down the verbatim duplication those three builders used to carry.
+ *
+ * Each event has one content builder returning `{ subject, html, text }`, and
+ * `renderNotification` dispatches by type. All untrusted candidate/job strings
+ * flow through `escapeHtml` before reaching the HTML.
+ */
+import { z } from 'zod'
+import type { NotificationType } from '~~/shared/notifications'
+
+export interface RenderedEmail {
+ subject: string
+ html: string
+ text: string
+}
+
+export function escapeHtml(str: string): string {
+ return str
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+}
+
+/**
+ * Shared responsive email shell. `bodyHtml` is trusted, pre-built markup;
+ * callers are responsible for escaping any untrusted values they interpolate.
+ */
+export function renderNotificationLayout(params: {
+ title: string
+ heading: string
+ bodyHtml: string
+ cta?: { label: string, url: string }
+ /** Small muted paragraph rendered below the CTA (already-escaped/plain text). */
+ note?: string
+ footer?: string
+}): string {
+ const { title, heading, bodyHtml, cta, note } = params
+ const footer = params.footer ?? 'Sent by Reqcore — Open-source applicant tracking'
+
+ const noteHtml = note
+ ? `${escapeHtml(note)}
`
+ : ''
+
+ const ctaHtml = cta
+ ? ``
+ : ''
+
+ return `
+
+
+
+
+ ${escapeHtml(title)}
+
+
+
+
+
+
+
+
+ Reqcore
+
+
+
+
+ ${escapeHtml(heading)}
+ ${bodyHtml}
+ ${ctaHtml}
+ ${noteHtml}
+
+
+
+
+ ${footer}
+
+
+
+
+
+
+
+`
+}
+
+// ─────────────────────────────────────────────
+// Per-event payloads + content builders
+// ─────────────────────────────────────────────
+
+export interface ApplicationCreatedPayload {
+ candidateName: string
+ jobTitle: string
+ applicationUrl: string
+}
+
+export interface CandidateRepliedPayload {
+ candidateName: string
+ jobTitle: string
+ /** Short preview of the reply, already trimmed by the producer. */
+ preview: string
+ applicationUrl: string
+}
+
+export interface InterviewResponsePayload {
+ candidateName: string
+ jobTitle: string
+ interviewTitle: string
+ response: 'accepted' | 'declined' | 'tentative'
+ interviewUrl: string
+}
+
+export interface NotificationPayloads {
+ application_created: ApplicationCreatedPayload
+ candidate_replied: CandidateRepliedPayload
+ interview_response: InterviewResponsePayload
+}
+
+const basePayloadSchema = z.object({
+ candidateName: z.string().min(1),
+ jobTitle: z.string().min(1),
+ applicationUrl: z.string().url(),
+})
+
+const payloadSchemas = {
+ application_created: basePayloadSchema,
+ candidate_replied: basePayloadSchema.extend({ preview: z.string() }),
+ interview_response: z.object({
+ candidateName: z.string().min(1),
+ jobTitle: z.string().min(1),
+ interviewTitle: z.string().min(1),
+ response: z.enum(['accepted', 'declined', 'tentative']),
+ interviewUrl: z.string().url(),
+ }),
+} as const
+
+function parsePayload(
+ type: T,
+ payload: Record,
+): NotificationPayloads[T] | null {
+ const result = payloadSchemas[type].safeParse(payload)
+ return result.success ? result.data as NotificationPayloads[T] : null
+}
+
+function paragraph(html: string): string {
+ return `${html}
`
+}
+
+function buildApplicationCreated(p: ApplicationCreatedPayload): RenderedEmail {
+ const subject = `New applicant for ${p.jobTitle}`
+ return {
+ subject,
+ html: renderNotificationLayout({
+ title: subject,
+ heading: 'New applicant',
+ bodyHtml:
+ paragraph(`${escapeHtml(p.candidateName)} applied for ${escapeHtml(p.jobTitle)} .`)
+ + paragraph('Open the application to review their profile.'),
+ cta: { label: 'View application', url: p.applicationUrl },
+ }),
+ text: [
+ 'New applicant',
+ '',
+ `${p.candidateName} applied for ${p.jobTitle}.`,
+ '',
+ `View the application: ${p.applicationUrl}`,
+ '',
+ '— Reqcore',
+ ].join('\n'),
+ }
+}
+
+function buildCandidateReplied(p: CandidateRepliedPayload): RenderedEmail {
+ const subject = `${p.candidateName} replied`
+ const previewHtml = p.preview
+ ? `${escapeHtml(p.preview)}
`
+ : ''
+ return {
+ subject,
+ html: renderNotificationLayout({
+ title: subject,
+ heading: 'New reply',
+ bodyHtml:
+ paragraph(`${escapeHtml(p.candidateName)} replied about ${escapeHtml(p.jobTitle)} .`)
+ + previewHtml
+ + paragraph('Open the conversation to respond.'),
+ cta: { label: 'Open conversation', url: p.applicationUrl },
+ }),
+ text: [
+ 'New reply',
+ '',
+ `${p.candidateName} replied about ${p.jobTitle}.`,
+ ...(p.preview ? ['', p.preview] : []),
+ '',
+ `Open the conversation: ${p.applicationUrl}`,
+ '',
+ '— Reqcore',
+ ].join('\n'),
+ }
+}
+
+const INTERVIEW_RESPONSE_COPY = {
+ accepted: { subject: 'accepted the interview', heading: 'Interview accepted', detail: 'confirmed' },
+ declined: { subject: 'declined the interview', heading: 'Interview declined', detail: 'declined' },
+ tentative: { subject: 'requested another time', heading: 'New time requested', detail: 'requested another time for' },
+} as const
+
+function buildInterviewResponse(p: InterviewResponsePayload): RenderedEmail {
+ const copy = INTERVIEW_RESPONSE_COPY[p.response]
+ const subject = `${p.candidateName} ${copy.subject}`
+ return {
+ subject,
+ html: renderNotificationLayout({
+ title: subject,
+ heading: copy.heading,
+ bodyHtml:
+ paragraph(`${escapeHtml(p.candidateName)} ${copy.detail} ${escapeHtml(p.interviewTitle)} for ${escapeHtml(p.jobTitle)} .`)
+ + paragraph('Open the interview to review the response and follow up.'),
+ cta: { label: 'Open interview', url: p.interviewUrl },
+ }),
+ text: [
+ copy.heading,
+ '',
+ `${p.candidateName} ${copy.detail} ${p.interviewTitle} for ${p.jobTitle}.`,
+ '',
+ `Open the interview: ${p.interviewUrl}`,
+ '',
+ '— Reqcore',
+ ].join('\n'),
+ }
+}
+
+/**
+ * Render an instant notification email for one outbox row. Returns null when the
+ * payload doesn't match the event's expected shape (defensive — a bad row is
+ * skipped, not retried forever).
+ */
+export function renderNotification(
+ type: NotificationType,
+ payload: Record,
+): RenderedEmail | null {
+ switch (type) {
+ case 'application_created': {
+ const parsed = parsePayload(type, payload)
+ return parsed ? buildApplicationCreated(parsed) : null
+ }
+ case 'candidate_replied': {
+ const parsed = parsePayload(type, payload)
+ return parsed ? buildCandidateReplied(parsed) : null
+ }
+ case 'interview_response': {
+ const parsed = parsePayload(type, payload)
+ return parsed ? buildInterviewResponse(parsed) : null
+ }
+ default:
+ return null
+ }
+}
+
+/** One-line summary of an event for the daily digest list. */
+export function summarizeNotification(type: NotificationType, payload: Record): string {
+ const p = payload as Record
+ const name = escapeHtml(String(p.candidateName ?? 'A candidate'))
+ const job = escapeHtml(String(p.jobTitle ?? 'a role'))
+ switch (type) {
+ case 'application_created':
+ return `${name} applied for ${job}`
+ case 'candidate_replied':
+ return `${name} replied about ${job}`
+ case 'interview_response': {
+ const response = p.response === 'accepted'
+ ? 'accepted'
+ : p.response === 'declined'
+ ? 'declined'
+ : 'requested another time for'
+ return `${name} ${response} ${escapeHtml(String(p.interviewTitle ?? 'the interview'))} for ${job}`
+ }
+ default:
+ return name
+ }
+}
+
+/**
+ * Render the daily digest email grouping several pending rows for one recipient.
+ * `dashboardUrl` deep-links to the dashboard so the CTA always works even though
+ * the individual rows may point at different applications.
+ */
+export function renderDigest(params: {
+ items: Array<{ type: NotificationType, payload: Record, applicationUrl: string }>
+ dashboardUrl: string
+}): RenderedEmail | null {
+ const validItems = params.items.every(item => parsePayload(item.type, {
+ ...item.payload,
+ applicationUrl: item.applicationUrl,
+ interviewUrl: item.applicationUrl,
+ }))
+ if (!validItems || params.items.length === 0) return null
+
+ const count = params.items.length
+ const subject = `Your Reqcore digest — ${count} update${count === 1 ? '' : 's'}`
+ const rowsHtml = params.items.map(item =>
+ ``
+ + `${summarizeNotification(item.type, item.payload)} `
+ + ` `,
+ ).join('')
+ const rowsText = params.items.map((item) => {
+ const p = item.payload as Record
+ return `• ${String(p.candidateName ?? 'A candidate')} — ${String(p.jobTitle ?? 'a role')}\n ${item.applicationUrl}`
+ }).join('\n')
+
+ return {
+ subject,
+ html: renderNotificationLayout({
+ title: subject,
+ heading: `${count} update${count === 1 ? '' : 's'} today`,
+ bodyHtml:
+ `Here's what happened across your roles.
`
+ + ``,
+ cta: { label: 'Open dashboard', url: params.dashboardUrl },
+ }),
+ text: [
+ `${count} update${count === 1 ? '' : 's'} today`,
+ '',
+ rowsText,
+ '',
+ `Open dashboard: ${params.dashboardUrl}`,
+ '',
+ '— Reqcore',
+ ].join('\n'),
+ }
+}
diff --git a/server/utils/notifications/urls.ts b/server/utils/notifications/urls.ts
new file mode 100644
index 00000000..4696b02c
--- /dev/null
+++ b/server/utils/notifications/urls.ts
@@ -0,0 +1,31 @@
+/**
+ * Dashboard deep-link helpers for notification emails. Mirrors the base-URL
+ * resolution used elsewhere (interview-conversation, google-calendar): explicit
+ * BETTER_AUTH_URL wins, then the Railway public domain, then the marketing URL.
+ *
+ * Producers persist only ids in the outbox payload; the worker builds the full
+ * URL at send time so links stay correct even if the deployment domain changes.
+ */
+export function appBaseUrl(): string {
+ return env.BETTER_AUTH_URL
+ || (env.RAILWAY_PUBLIC_DOMAIN ? `https://${env.RAILWAY_PUBLIC_DOMAIN}` : '')
+ || 'https://reqcore.com'
+}
+
+export function applicationUrl(applicationId: string): string {
+ return `${appBaseUrl()}/dashboard/applications/${applicationId}`
+}
+
+export function interviewUrl(interviewId: string): string {
+ return `${appBaseUrl()}/dashboard/interviews/${interviewId}`
+}
+
+export function dashboardUrl(): string {
+ return `${appBaseUrl()}/dashboard`
+}
+
+export function inboxConversationUrl(conversationId: string): string {
+ const url = new URL('/dashboard/inbox', appBaseUrl())
+ url.searchParams.set('conversation', conversationId)
+ return url.toString()
+}
diff --git a/shared/notifications.ts b/shared/notifications.ts
new file mode 100644
index 00000000..cc99745c
--- /dev/null
+++ b/shared/notifications.ts
@@ -0,0 +1,74 @@
+/**
+ * Shared notification constants — single source of truth for the recruiter
+ * notification engine's event types, delivery modes, and per-type defaults.
+ *
+ * Imported by both the server (recipient resolution, dispatch) and the client
+ * (preferences UI) so the two never drift.
+ */
+
+/** Recruiter-facing events supported by the notification engine. */
+export const NOTIFICATION_TYPES = [
+ 'candidate_replied',
+ 'application_created',
+ 'interview_response',
+] as const
+
+export type NotificationType = (typeof NOTIFICATION_TYPES)[number]
+
+/** How a recipient wants a given event delivered. `off` disables it entirely. */
+export type NotificationChannelMode = 'instant' | 'digest' | 'off'
+
+/** Delivery cadence of an individual outbox row (never `off` — off rows are not enqueued). */
+export type NotificationCadence = 'instant' | 'digest'
+
+/**
+ * Default delivery mode when a recipient has no explicit preference row.
+ * New-applicant is batched by default (high volume); replies and finished
+ * scoring are instant because they're the pull-back-in moments.
+ */
+export const DEFAULT_CHANNEL_MODE: Record = {
+ candidate_replied: 'instant',
+ application_created: 'digest',
+ interview_response: 'instant',
+}
+
+/** Delivery choices exposed and accepted for each event. */
+export const NOTIFICATION_CHANNEL_MODES: Record = {
+ candidate_replied: ['off', 'instant', 'digest'],
+ application_created: ['off', 'digest'],
+ interview_response: ['off', 'instant', 'digest'],
+}
+
+/** Human-readable copy for the preferences UI. */
+export const NOTIFICATION_TYPE_META: Record = {
+ candidate_replied: {
+ label: 'Candidate replied',
+ description: 'A candidate answered one of your messages.',
+ },
+ application_created: {
+ label: 'New applicant',
+ description: 'Someone applied to one of your open roles.',
+ },
+ interview_response: {
+ label: 'Interview response',
+ description: 'A candidate accepts, declines, or requests another time for an interview.',
+ },
+}
+
+export function isNotificationType(value: string): value is NotificationType {
+ return (NOTIFICATION_TYPES as readonly string[]).includes(value)
+}
+
+export function isNotificationChannelModeAllowed(
+ type: NotificationType,
+ mode: NotificationChannelMode,
+): boolean {
+ return NOTIFICATION_CHANNEL_MODES[type].includes(mode)
+}
+
+export function normalizeNotificationChannelMode(
+ type: NotificationType,
+ mode: NotificationChannelMode,
+): NotificationChannelMode {
+ return isNotificationChannelModeAllowed(type, mode) ? mode : DEFAULT_CHANNEL_MODE[type]
+}
diff --git a/tests/unit/application-deletion.test.ts b/tests/unit/application-deletion.test.ts
new file mode 100644
index 00000000..6f262574
--- /dev/null
+++ b/tests/unit/application-deletion.test.ts
@@ -0,0 +1,137 @@
+import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
+
+type Handler = (event: unknown) => Promise
+
+let handler: Handler
+
+beforeAll(async () => {
+ vi.stubGlobal('defineEventHandler', (value: Handler) => value)
+ handler = (await import('../../server/api/applications/[id].delete')).default as Handler
+})
+
+function httpError(input: { statusCode: number, statusMessage: string }) {
+ return Object.assign(new Error(input.statusMessage), input)
+}
+
+function makeDb(options: {
+ applicationExists?: boolean
+ interviews?: Array<{
+ id: string
+ status: 'scheduled' | 'completed' | 'cancelled' | 'no_show'
+ invitationSentAt: Date | null
+ googleCalendarEventId: string | null
+ createdById: string
+ }>
+ attachmentKeys?: string[]
+} = {}) {
+ const txDelete = vi.fn(() => ({
+ where: vi.fn(() => Object.assign(Promise.resolve(), {
+ returning: vi.fn(async () => [{ id: 'application-1' }]),
+ })),
+ }))
+ const transaction = vi.fn(async (callback: (tx: { delete: typeof txDelete }) => Promise) => {
+ await callback({ delete: txDelete })
+ })
+
+ return {
+ txDelete,
+ transaction,
+ db: {
+ query: {
+ application: {
+ findFirst: vi.fn(async () => options.applicationExists === false ? undefined : { id: 'application-1' }),
+ },
+ interview: {
+ findMany: vi.fn(async () => options.interviews ?? []),
+ },
+ candidateConversation: {
+ findMany: vi.fn(async () => options.attachmentKeys?.length ? [{ id: 'conversation-1' }] : []),
+ },
+ candidateMessage: {
+ findMany: vi.fn(async () => options.attachmentKeys?.length ? [{ id: 'message-1' }] : []),
+ },
+ candidateMessageAttachment: {
+ findMany: vi.fn(async () => (options.attachmentKeys ?? []).map(storageKey => ({ storageKey }))),
+ },
+ },
+ transaction,
+ },
+ }
+}
+
+beforeEach(() => {
+ vi.stubGlobal('requirePermission', vi.fn(async () => ({
+ session: { activeOrganizationId: 'org-1' },
+ user: { id: 'user-1' },
+ })))
+ vi.stubGlobal('getValidatedRouterParams', vi.fn(async () => ({ id: 'application-1' })))
+ vi.stubGlobal('createError', httpError)
+ vi.stubGlobal('setResponseStatus', vi.fn())
+ vi.stubGlobal('recordActivity', vi.fn())
+ vi.stubGlobal('deleteFromS3', vi.fn(async () => {}))
+ vi.stubGlobal('logWarn', vi.fn())
+ vi.stubGlobal('logError', vi.fn())
+})
+
+afterEach(() => {
+ vi.unstubAllGlobals()
+})
+
+describe('DELETE /api/applications/:id', () => {
+ it('requires application delete permission and removes the dependent graph transactionally', async () => {
+ const mock = makeDb()
+ vi.stubGlobal('db', mock.db)
+
+ await expect(handler({})).resolves.toBeNull()
+
+ expect(requirePermission).toHaveBeenCalledWith({}, { application: ['delete'] })
+ expect(mock.transaction).toHaveBeenCalledOnce()
+ expect(mock.txDelete).toHaveBeenCalledTimes(3)
+ expect(setResponseStatus).toHaveBeenCalledWith({}, 204)
+ expect(recordActivity).toHaveBeenCalledWith(expect.objectContaining({
+ organizationId: 'org-1',
+ action: 'deleted',
+ resourceType: 'application',
+ resourceId: 'application-1',
+ }))
+ })
+
+ it('returns 404 without deleting when the application is outside the organization', async () => {
+ const mock = makeDb({ applicationExists: false })
+ vi.stubGlobal('db', mock.db)
+
+ await expect(handler({})).rejects.toMatchObject({ statusCode: 404 })
+ expect(mock.transaction).not.toHaveBeenCalled()
+ })
+
+ it('requires an invited scheduled interview to be cancelled first', async () => {
+ const mock = makeDb({
+ interviews: [{
+ id: 'interview-1',
+ status: 'scheduled',
+ invitationSentAt: new Date(),
+ googleCalendarEventId: null,
+ createdById: 'user-1',
+ }],
+ })
+ vi.stubGlobal('db', mock.db)
+
+ await expect(handler({})).rejects.toMatchObject({ statusCode: 409 })
+ expect(mock.transaction).not.toHaveBeenCalled()
+ })
+
+ it('best-effort removes message attachments without blocking database deletion', async () => {
+ const mock = makeDb({ attachmentKeys: ['applications/message.txt'] })
+ vi.stubGlobal('db', mock.db)
+ vi.mocked(deleteFromS3).mockRejectedValueOnce(new Error('storage unavailable'))
+
+ await expect(handler({})).resolves.toBeNull()
+
+ expect(deleteFromS3).toHaveBeenCalledWith('applications/message.txt')
+ expect(logWarn).toHaveBeenCalledWith(
+ 'application.attachment_delete_failed',
+ expect.objectContaining({ application_id: 'application-1' }),
+ )
+ expect(mock.transaction).toHaveBeenCalledOnce()
+ })
+})
diff --git a/tests/unit/demo-account-isolation.test.ts b/tests/unit/demo-account-isolation.test.ts
index 250c8e7c..0621487c 100644
--- a/tests/unit/demo-account-isolation.test.ts
+++ b/tests/unit/demo-account-isolation.test.ts
@@ -27,4 +27,21 @@ describe('demo account organization isolation', () => {
expect(read('server/api/invite-links/accept.post.ts')).toMatch(/The demo account cannot join other organizations/)
expect(read('server/api/join-requests/index.post.ts')).toMatch(/The demo account cannot request access/)
})
+
+ it('suppresses the onboarding survey (and its re-prompt banner) for the demo account', () => {
+ const surveyGet = read('server/api/onboarding-survey/index.get.ts')
+
+ expect(surveyGet).toMatch(/isDemoAccountEmail\(session\.user\.email\)/)
+ expect(surveyGet).toMatch(/return \{ completed: true \}/)
+ })
+
+ it('keeps demo account email notification preferences disabled', () => {
+ const seed = read('server/scripts/seed.ts')
+
+ expect(seed).toMatch(/disableDemoEmailNotifications\(userId, existingOrg\.id\)/)
+ expect(seed).toMatch(/disableDemoEmailNotifications\(userId, orgId\)/)
+ expect(seed).toMatch(/set: \{ channelMode: "off", updatedAt: now \}/)
+ expect(read('server/api/notification-preferences/index.get.ts')).toMatch(/isDemoAccountEmail\(session\.user\.email\)/)
+ expect(read('server/api/notification-preferences/index.patch.ts')).toMatch(/isDemoAccountEmail\(session\.user\.email\)/)
+ })
})
diff --git a/tests/unit/notification-delivery-status.test.ts b/tests/unit/notification-delivery-status.test.ts
new file mode 100644
index 00000000..5298db02
--- /dev/null
+++ b/tests/unit/notification-delivery-status.test.ts
@@ -0,0 +1,96 @@
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import type { NotificationStatusEvent } from '../../server/utils/notifications/delivery-status'
+import { processNotificationStatusEvent } from '../../server/utils/notifications/delivery-status'
+
+function statusEvent(type: NotificationStatusEvent['type'], extra: Record = {}) {
+ return {
+ type,
+ created_at: '2026-01-02T09:00:00.000Z',
+ data: {
+ email_id: 'provider_1',
+ ...extra,
+ },
+ } as unknown as NotificationStatusEvent
+}
+
+function makeDb(row: Record | undefined) {
+ const setCalls: Array> = []
+ const inserted: Array> = []
+ return {
+ setCalls,
+ inserted,
+ db: {
+ query: { notificationOutbox: { findFirst: vi.fn().mockResolvedValue(row) } },
+ update: () => ({
+ set: (values: Record) => {
+ setCalls.push(values)
+ return { where: () => Promise.resolve() }
+ },
+ }),
+ insert: () => ({
+ values: (values: Record) => {
+ inserted.push(values)
+ return { onConflictDoNothing: () => Promise.resolve() }
+ },
+ }),
+ },
+ }
+}
+
+afterEach(() => vi.unstubAllGlobals())
+
+describe('processNotificationStatusEvent', () => {
+ const instantRow = {
+ id: 'outbox_1',
+ organizationId: 'org1',
+ recipientUserId: 'u1',
+ recipientEmail: 'Recruiter@Example.COM ',
+ cadence: 'instant',
+ digestBucket: null,
+ }
+
+ it('marks a permanent bounce dead and suppresses the normalized address', async () => {
+ const { db, setCalls, inserted } = makeDb(instantRow)
+ vi.stubGlobal('db', db)
+
+ await processNotificationStatusEvent(statusEvent('email.bounced', {
+ bounce: { type: 'Permanent', message: 'Mailbox does not exist' },
+ }))
+
+ expect(setCalls[0]).toMatchObject({ status: 'dead', lastError: 'Permanent: Mailbox does not exist' })
+ expect(inserted[0]).toEqual({ email: 'recruiter@example.com', reason: 'bounce' })
+ })
+
+ it('marks a soft bounce dead without permanently suppressing the address', async () => {
+ const { db, setCalls, inserted } = makeDb(instantRow)
+ vi.stubGlobal('db', db)
+
+ await processNotificationStatusEvent(statusEvent('email.bounced', {
+ bounce: { type: 'Transient', message: 'Try again later' },
+ }))
+
+ expect(setCalls[0]).toMatchObject({ status: 'dead' })
+ expect(inserted).toHaveLength(0)
+ })
+
+ it('keeps a delivered outbox row sent', async () => {
+ const { db, setCalls } = makeDb(instantRow)
+ vi.stubGlobal('db', db)
+
+ await processNotificationStatusEvent(statusEvent('email.delivered'))
+
+ expect(setCalls[0]!.status).toBeUndefined()
+ expect(setCalls[0]!.failedAt).toBeUndefined()
+ })
+
+ it('updates a digest delivery through its leader row', async () => {
+ const { db, setCalls } = makeDb({ ...instantRow, cadence: 'digest', digestBucket: '2026-01-02' })
+ vi.stubGlobal('db', db)
+
+ await processNotificationStatusEvent(statusEvent('email.failed', {
+ failed: { reason: 'rejected' },
+ }))
+
+ expect(setCalls[0]).toMatchObject({ status: 'dead', lastError: 'failed: rejected' })
+ })
+})
diff --git a/tests/unit/notification-dispatch.test.ts b/tests/unit/notification-dispatch.test.ts
new file mode 100644
index 00000000..c618b0d6
--- /dev/null
+++ b/tests/unit/notification-dispatch.test.ts
@@ -0,0 +1,216 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+const { sendMock } = vi.hoisted(() => ({
+ sendMock: vi.fn(),
+}))
+vi.mock('../../server/utils/email', () => ({ sendNotificationEmail: sendMock }))
+
+const { runNotificationDispatch, runNotificationDigest } = await import('../../server/utils/notifications/dispatch')
+
+function queryable(result: unknown) {
+ const p = Promise.resolve(result)
+ const proxy: unknown = new Proxy(() => {}, {
+ get(_t, prop) {
+ if (prop === 'then') return p.then.bind(p)
+ if (prop === 'catch') return p.catch.bind(p)
+ if (prop === 'finally') return p.finally.bind(p)
+ return () => proxy
+ },
+ })
+ return proxy
+}
+
+function makeDb(selectResults: unknown[][], returningResults: unknown[][] = []) {
+ const setCalls: Array> = []
+ let selectIndex = 0
+ let returningIndex = 0
+
+ const database: Record = {
+ select: () => queryable(selectResults[selectIndex++] ?? []),
+ update: () => ({
+ set: (vals: Record) => {
+ setCalls.push(vals)
+ return {
+ where: () => {
+ const promise = Promise.resolve()
+ return Object.assign(promise, {
+ returning: () => Promise.resolve(returningResults[returningIndex++] ?? []),
+ })
+ },
+ }
+ },
+ }),
+ }
+ database.transaction = async (callback: (tx: unknown) => unknown) => callback(database)
+ return { db: database, setCalls }
+}
+
+function pendingRow(overrides: Record = {}) {
+ return {
+ id: 'outbox_1',
+ organizationId: 'org1',
+ recipientUserId: 'u1',
+ recipientEmail: 'rec@example.com',
+ type: 'application_created',
+ cadence: 'instant',
+ payload: { applicationId: 'app1', candidateName: 'Jane', jobTitle: 'Engineer' },
+ status: 'pending',
+ attempts: 0,
+ nextAttemptAt: new Date('2026-01-01T00:00:00Z'),
+ digestBucket: null,
+ providerMessageId: null,
+ sentAt: null,
+ failedAt: null,
+ lastError: null,
+ createdAt: new Date('2026-01-01T00:00:00Z'),
+ updatedAt: new Date('2026-01-01T00:00:00Z'),
+ ...overrides,
+ }
+}
+
+beforeEach(() => {
+ vi.stubGlobal('logInfo', vi.fn())
+ vi.stubGlobal('logWarn', vi.fn())
+ vi.stubGlobal('logError', vi.fn())
+})
+
+afterEach(() => {
+ vi.unstubAllGlobals()
+ vi.useRealTimers()
+ sendMock.mockReset()
+})
+
+describe('runNotificationDispatch', () => {
+ it('does nothing when the global kill-switch is off', async () => {
+ vi.stubGlobal('env', { NOTIFICATIONS_ENABLED: false })
+ const select = vi.fn()
+ vi.stubGlobal('db', { select })
+
+ const result = await runNotificationDispatch({ source: 'interactive' })
+
+ expect(result.enabled).toBe(false)
+ expect(select).not.toHaveBeenCalled()
+ })
+
+ it('claims and marks an instant row sent with its provider message id', async () => {
+ vi.stubGlobal('env', { NOTIFICATIONS_ENABLED: true, BETTER_AUTH_URL: 'https://app.example.com' })
+ const row = pendingRow()
+ const { db, setCalls } = makeDb([[{ id: row.id }], [], []], [[row]])
+ vi.stubGlobal('db', db)
+ sendMock.mockResolvedValue({ providerMessageId: 'prov_1' })
+
+ const result = await runNotificationDispatch({ source: 'interactive' })
+
+ expect(result.sent).toBe(1)
+ expect(setCalls[0]!.nextAttemptAt).toBeInstanceOf(Date)
+ expect(setCalls[1]).toMatchObject({ status: 'sent', providerMessageId: 'prov_1' })
+ })
+
+ it('links invitation responses directly to the interview', async () => {
+ vi.stubGlobal('env', { NOTIFICATIONS_ENABLED: true, BETTER_AUTH_URL: 'https://app.example.com' })
+ const row = pendingRow({
+ type: 'interview_response',
+ payload: {
+ interviewId: 'interview1',
+ candidateName: 'Jane',
+ jobTitle: 'Engineer',
+ interviewTitle: 'Technical interview',
+ response: 'accepted',
+ },
+ })
+ const { db } = makeDb([[{ id: row.id }], [], []], [[row]])
+ vi.stubGlobal('db', db)
+ sendMock.mockResolvedValue({ providerMessageId: 'prov_2' })
+
+ const result = await runNotificationDispatch({ source: 'interactive' })
+
+ expect(result.sent).toBe(1)
+ expect(sendMock).toHaveBeenCalledWith(expect.objectContaining({
+ html: expect.stringContaining('https://app.example.com/dashboard/interviews/interview1'),
+ type: 'interview_response',
+ }))
+ })
+
+ it('does not send when another worker wins the claim', async () => {
+ vi.stubGlobal('env', { NOTIFICATIONS_ENABLED: true })
+ const { db } = makeDb([[{ id: 'outbox_1' }], []], [[]])
+ vi.stubGlobal('db', db)
+
+ const result = await runNotificationDispatch({ source: 'interactive' })
+
+ expect(result.processed).toBe(0)
+ expect(sendMock).not.toHaveBeenCalled()
+ })
+
+ it('schedules the first failed attempt one minute later', async () => {
+ vi.useFakeTimers()
+ vi.setSystemTime(new Date('2026-01-01T12:00:00Z'))
+ vi.stubGlobal('env', { NOTIFICATIONS_ENABLED: true, BETTER_AUTH_URL: 'https://app.example.com' })
+ const row = pendingRow()
+ const { db, setCalls } = makeDb([[{ id: row.id }], [], []], [[row]])
+ vi.stubGlobal('db', db)
+ sendMock.mockRejectedValue(new Error('resend down'))
+
+ const result = await runNotificationDispatch({ source: 'interactive' })
+
+ expect(result.retried).toBe(1)
+ expect(setCalls[1]).toMatchObject({ attempts: 1 })
+ expect((setCalls[1]!.nextAttemptAt as Date).getTime()).toBe(Date.now() + 60_000)
+ })
+
+ it('dead-letters a row on its sixth failed attempt', async () => {
+ vi.stubGlobal('env', { NOTIFICATIONS_ENABLED: true, BETTER_AUTH_URL: 'https://app.example.com' })
+ const row = pendingRow({ attempts: 5 })
+ const { db, setCalls } = makeDb([[{ id: row.id }], [], []], [[row]])
+ vi.stubGlobal('db', db)
+ sendMock.mockRejectedValue(new Error('resend down'))
+
+ const result = await runNotificationDispatch({ source: 'interactive' })
+
+ expect(result.dead).toBe(1)
+ expect(setCalls[1]).toMatchObject({ status: 'dead', attempts: 6 })
+ })
+
+ it('skips a claimed row whose address became suppressed', async () => {
+ vi.stubGlobal('env', { NOTIFICATIONS_ENABLED: true })
+ const row = pendingRow()
+ const { db, setCalls } = makeDb([[{ id: row.id }], [{ id: 'suppression_1' }], []], [[row]])
+ vi.stubGlobal('db', db)
+
+ const result = await runNotificationDispatch({ source: 'interactive' })
+
+ expect(result.skipped).toBe(1)
+ expect(setCalls[1]).toMatchObject({ status: 'skipped', lastError: 'email_suppressed' })
+ expect(sendMock).not.toHaveBeenCalled()
+ })
+})
+
+describe('runNotificationDigest', () => {
+ it('isolates a digest group and uses a stable organization/user/bucket key', async () => {
+ vi.stubGlobal('env', { NOTIFICATIONS_ENABLED: true, BETTER_AUTH_URL: 'https://app.example.com' })
+ const row = pendingRow({
+ cadence: 'digest',
+ digestBucket: '2026-01-02',
+ })
+ const group = {
+ organizationId: row.organizationId,
+ recipientUserId: row.recipientUserId,
+ recipientEmail: row.recipientEmail,
+ digestBucket: row.digestBucket,
+ }
+ const { db, setCalls } = makeDb([[group], []], [[row]])
+ vi.stubGlobal('db', db)
+ sendMock.mockResolvedValue({ providerMessageId: 'digest_provider' })
+
+ const result = await runNotificationDigest({ source: 'interactive' })
+
+ expect(result.sent).toBe(1)
+ expect(sendMock).toHaveBeenCalledWith(expect.objectContaining({
+ idempotencyKey: 'digest:org1:u1:2026-01-02',
+ }))
+ expect(setCalls).toEqual(expect.arrayContaining([
+ expect.objectContaining({ status: 'sent', providerMessageId: null }),
+ expect.objectContaining({ providerMessageId: 'digest_provider' }),
+ ]))
+ })
+})
diff --git a/tests/unit/notification-enqueue.test.ts b/tests/unit/notification-enqueue.test.ts
new file mode 100644
index 00000000..fab07fbd
--- /dev/null
+++ b/tests/unit/notification-enqueue.test.ts
@@ -0,0 +1,135 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+const { recipientsMock } = vi.hoisted(() => ({ recipientsMock: vi.fn() }))
+vi.mock('../../server/utils/notifications/recipients', () => ({
+ resolveRecipients: recipientsMock,
+}))
+
+const { digestBucketFor, digestDeliveryAt, enqueueNotification } = await import('../../server/utils/notifications/enqueue')
+const { enqueueInterviewResponseNotification } = await import('../../server/utils/notifications/interview-response')
+
+function insertionDb() {
+ const stored = new Map>()
+ return {
+ stored,
+ db: {
+ insert: () => ({
+ values: (rows: Array>) => ({
+ onConflictDoNothing: async () => {
+ for (const row of rows) {
+ const key = String(row.dedupeKey)
+ if (!stored.has(key)) stored.set(key, row)
+ }
+ },
+ }),
+ }),
+ },
+ }
+}
+
+beforeEach(() => {
+ vi.stubGlobal('logWarn', vi.fn())
+ recipientsMock.mockResolvedValue([
+ { userId: 'u1', email: 'one@example.com', cadence: 'instant' },
+ { userId: 'u2', email: 'two@example.com', cadence: 'digest' },
+ ])
+})
+
+afterEach(() => {
+ vi.unstubAllGlobals()
+ recipientsMock.mockReset()
+})
+
+describe('enqueueNotification', () => {
+ it('deduplicates each recipient and schedules digest delivery at 08:00 UTC', async () => {
+ const { db, stored } = insertionDb()
+ vi.stubGlobal('db', db)
+ const event = {
+ organizationId: 'org1',
+ type: 'application_created' as const,
+ dedupeKey: 'application_created:app1',
+ payload: { applicationId: 'app1' },
+ }
+
+ await enqueueNotification(event)
+ await enqueueNotification(event)
+
+ expect(stored.size).toBe(2)
+ expect([...stored.keys()]).toEqual([
+ 'application_created:app1:u1',
+ 'application_created:app1:u2',
+ ])
+ const digest = stored.get('application_created:app1:u2')!
+ expect(digest.digestBucket).toBeTypeOf('string')
+ expect((digest.nextAttemptAt as Date).toISOString()).toBe(`${digest.digestBucket}T08:00:00.000Z`)
+ })
+
+ it('logs and swallows recipient resolution failures', async () => {
+ recipientsMock.mockRejectedValue(new Error('database unavailable'))
+ vi.stubGlobal('db', {})
+
+ await expect(enqueueNotification({
+ organizationId: 'org1',
+ type: 'candidate_replied',
+ dedupeKey: 'candidate_replied:m1',
+ payload: {},
+ })).resolves.toBeUndefined()
+ expect(logWarn).toHaveBeenCalledWith('notification.enqueue_failed', expect.any(Object))
+ })
+
+ it('logs and swallows outbox insertion failures', async () => {
+ vi.stubGlobal('db', {
+ insert: () => ({
+ values: () => ({
+ onConflictDoNothing: async () => {
+ throw new Error('database unavailable')
+ },
+ }),
+ }),
+ })
+
+ await expect(enqueueNotification({
+ organizationId: 'org1',
+ type: 'candidate_replied',
+ dedupeKey: 'candidate_replied:m1',
+ payload: {},
+ })).resolves.toBeUndefined()
+ expect(logWarn).toHaveBeenCalledWith('notification.enqueue_failed', expect.any(Object))
+ })
+
+ it('uses a bounded interview-and-response dedupe key for invitation responses', async () => {
+ recipientsMock.mockResolvedValue([
+ { userId: 'u1', email: 'one@example.com', cadence: 'instant' },
+ ])
+ const { db, stored } = insertionDb()
+ vi.stubGlobal('db', db)
+
+ await enqueueInterviewResponseNotification({
+ organizationId: 'org1',
+ interviewId: 'interview1',
+ candidateName: 'Jane Doe',
+ jobTitle: 'Engineer',
+ interviewTitle: 'Technical interview',
+ response: 'accepted',
+ })
+
+ expect(stored.get('interview_response:interview1:accepted:u1')).toMatchObject({
+ type: 'interview_response',
+ payload: {
+ interviewId: 'interview1',
+ candidateName: 'Jane Doe',
+ jobTitle: 'Engineer',
+ interviewTitle: 'Technical interview',
+ response: 'accepted',
+ },
+ })
+ })
+})
+
+describe('digest bucketing', () => {
+ it('uses today before 08:00 UTC and tomorrow at or after the cutoff', () => {
+ expect(digestBucketFor(new Date('2026-01-02T07:59:59Z'))).toBe('2026-01-02')
+ expect(digestBucketFor(new Date('2026-01-02T08:00:00Z'))).toBe('2026-01-03')
+ expect(digestDeliveryAt('2026-01-03').toISOString()).toBe('2026-01-03T08:00:00.000Z')
+ })
+})
diff --git a/tests/unit/notification-recipients.test.ts b/tests/unit/notification-recipients.test.ts
new file mode 100644
index 00000000..466d83de
--- /dev/null
+++ b/tests/unit/notification-recipients.test.ts
@@ -0,0 +1,86 @@
+import { describe, expect, it } from 'vitest'
+import { resolveRecipients, type NotificationDbClient } from '../../server/utils/notifications/recipients'
+
+/**
+ * Minimal fake Drizzle client whose `.select()` chains resolve to a queued list
+ * of results, in call order. resolveRecipients runs exactly three selects:
+ * members, preferences, then suppressed addresses.
+ */
+function fakeDb(resultsInOrder: unknown[]): NotificationDbClient {
+ let i = 0
+ function makeChain() {
+ const current = resultsInOrder[i]
+ const p = Promise.resolve(current)
+ const proxy: unknown = new Proxy(() => {}, {
+ get(_t, prop) {
+ if (prop === 'then') { i++; return p.then.bind(p) }
+ if (prop === 'catch') return p.catch.bind(p)
+ if (prop === 'finally') return p.finally.bind(p)
+ return () => proxy
+ },
+ })
+ return proxy
+ }
+ return { select: () => makeChain() } as unknown as NotificationDbClient
+}
+
+describe('resolveRecipients', () => {
+ it('applies defaults, honors off, normalizes legacy instant applications, and excludes suppressed addresses', async () => {
+ const members = [
+ { userId: 'a', email: 'a@example.com' }, // no pref → default (digest for application_created)
+ { userId: 'b', email: 'b@example.com' }, // pref: off → excluded
+ { userId: 'c', email: 'c@example.com' }, // legacy pref: instant -> digest
+ { userId: 'd', email: 'd@example.com' }, // suppressed → excluded
+ ]
+ const prefs = [
+ { userId: 'b', channelMode: 'off' },
+ { userId: 'c', channelMode: 'instant' },
+ ]
+ const suppressed = [{ email: 'd@example.com' }]
+
+ const recipients = await resolveRecipients(
+ 'org1',
+ 'application_created',
+ fakeDb([members, prefs, suppressed]),
+ )
+
+ expect(recipients).toEqual([
+ { userId: 'a', email: 'a@example.com', cadence: 'digest' },
+ { userId: 'c', email: 'c@example.com', cadence: 'digest' },
+ ])
+ })
+
+ it('returns no recipients when the org has no members', async () => {
+ const recipients = await resolveRecipients('org1', 'candidate_replied', fakeDb([[]]))
+ expect(recipients).toEqual([])
+ })
+
+ it('never returns the shared demo account as an email recipient', async () => {
+ const members = [
+ { userId: 'demo', email: 'demo@reqcore.com' },
+ { userId: 'recruiter', email: 'recruiter@example.com' },
+ ]
+
+ const recipients = await resolveRecipients(
+ 'org1',
+ 'candidate_replied',
+ fakeDb([members, [], []]),
+ )
+
+ expect(recipients).toEqual([
+ { userId: 'recruiter', email: 'recruiter@example.com', cadence: 'instant' },
+ ])
+ })
+
+ it('delivers interview responses instantly by default', async () => {
+ const recipients = await resolveRecipients(
+ 'org1',
+ 'interview_response',
+ fakeDb([[{ userId: 'recruiter', email: 'recruiter@example.com' }], [], []]),
+ )
+
+ expect(recipients).toEqual([
+ { userId: 'recruiter', email: 'recruiter@example.com', cadence: 'instant' },
+ ])
+ })
+})
diff --git a/tests/unit/notification-templates.test.ts b/tests/unit/notification-templates.test.ts
new file mode 100644
index 00000000..74c65e1c
--- /dev/null
+++ b/tests/unit/notification-templates.test.ts
@@ -0,0 +1,87 @@
+import { describe, expect, it } from 'vitest'
+import { renderNotification, renderDigest } from '../../server/utils/notifications/templates'
+
+describe('notification templates', () => {
+ it('renders an application_created email with subject, deep link, and details', () => {
+ const rendered = renderNotification('application_created', {
+ candidateName: 'Jane Doe',
+ jobTitle: 'Staff Engineer',
+ applicationUrl: 'https://app.example.com/dashboard/applications/abc',
+ })
+ expect(rendered).not.toBeNull()
+ expect(rendered!.subject).toBe('New applicant for Staff Engineer')
+ expect(rendered!.html).toContain('Jane Doe')
+ expect(rendered!.html).toContain('https://app.example.com/dashboard/applications/abc')
+ expect(rendered!.text).toContain('Jane Doe applied for Staff Engineer')
+ })
+
+ it('escapes untrusted candidate and job strings in the HTML', () => {
+ const rendered = renderNotification('application_created', {
+ candidateName: '',
+ jobTitle: 'Dev "> ',
+ applicationUrl: 'https://app.example.com/dashboard/applications/abc',
+ })
+ expect(rendered!.html).not.toContain('')
+ expect(rendered!.html).toContain('<script>alert(1)</script>')
+ expect(rendered!.html).not.toContain(' ')
+ })
+
+ it.each([
+ ['accepted', 'Interview accepted', 'Jane Doe accepted the interview'],
+ ['declined', 'Interview declined', 'Jane Doe declined the interview'],
+ ['tentative', 'New time requested', 'Jane Doe requested another time'],
+ ] as const)('renders an %s interview response with a direct interview link', (response, heading, subject) => {
+ const rendered = renderNotification('interview_response', {
+ candidateName: 'Jane Doe',
+ jobTitle: 'Staff Engineer',
+ interviewTitle: 'Technical interview',
+ response,
+ interviewUrl: 'https://app.example.com/dashboard/interviews/interview-1',
+ })
+
+ expect(rendered).not.toBeNull()
+ expect(rendered!.subject).toBe(subject)
+ expect(rendered!.html).toContain(heading)
+ expect(rendered!.html).toContain('Technical interview')
+ expect(rendered!.html).toContain('https://app.example.com/dashboard/interviews/interview-1')
+ expect(rendered!.text).toContain('Staff Engineer')
+ })
+
+ it('rolls several events into one digest email', () => {
+ const digest = renderDigest({
+ items: [
+ { type: 'application_created', payload: { candidateName: 'A', jobTitle: 'Role 1' }, applicationUrl: 'https://x/1' },
+ { type: 'candidate_replied', payload: { candidateName: 'B', jobTitle: 'Role 2', preview: 'Thanks' }, applicationUrl: 'https://x/2' },
+ { type: 'interview_response', payload: { candidateName: 'C', jobTitle: 'Role 3', interviewTitle: 'Screening', response: 'accepted' }, applicationUrl: 'https://x/3' },
+ ],
+ dashboardUrl: 'https://x/dashboard',
+ })
+ expect(digest!.subject).toContain('3 updates')
+ expect(digest!.html).toContain('Role 1')
+ expect(digest!.html).toContain('Role 2')
+ expect(digest!.html).toContain('Screening')
+ expect(digest!.html).toContain('https://x/dashboard')
+ })
+
+ it('rejects malformed instant and digest payloads', () => {
+ expect(renderNotification('application_created', {
+ candidateName: 'Jane',
+ applicationUrl: 'https://x/app',
+ })).toBeNull()
+ expect(renderDigest({
+ items: [{
+ type: 'candidate_replied',
+ payload: { candidateName: 'Jane', jobTitle: 'Engineer' },
+ applicationUrl: 'https://x/app',
+ }],
+ dashboardUrl: 'https://x/dashboard',
+ })).toBeNull()
+ expect(renderNotification('interview_response', {
+ candidateName: 'Jane',
+ jobTitle: 'Engineer',
+ interviewTitle: 'Screening',
+ response: 'pending',
+ interviewUrl: 'https://x/interview',
+ })).toBeNull()
+ })
+})