Reference
Types Reference
Complete TypeScript type definitions for the Nexa Ed SDK.
All types are exported from @nexa-ed/sdk (and re-exported from @nexa-ed/next).
File types
type FileProgressEvent =
| { type: "progress"; stage: string; progress: number; message: string }
| { type: "complete"; records: StudentRecord[] }
| { type: "error"; message: string };
type DocumentSummary = {
fileId: string;
documentTitle: string;
status: "queued" | "processing" | "complete" | "failed";
pageCount: number;
recordCount: number;
createdAt: string;
};
type StudentRecord = {
recordId: string;
fileId: string;
pageNumber: number;
studentName: string;
subjects: Record<string, number | null>;
total: number | null;
grade: string | null;
position: number | null;
rawText: string;
};Payment types
type PaymentMetadata = {
userId?: string;
studentName?: string;
category?: "enrollment" | "exam" | "tuition" | "other";
[key: string]: string | undefined;
};
type PaymentTransaction = {
reference: string;
tenantId: string;
customerEmail: string;
amount: number;
amountPaid: number;
status: "success" | "failed" | "pending" | "abandoned";
fees: number;
platformFee: number;
netAmount: number;
paidAt: string | null;
channel: string;
failureReason: string | null;
createdAt: string;
updatedAt: string;
};
type PaymentConfig = {
platformFeePercent: number;
platformFeeFixed: number;
currency: string;
bankDetails?: {
bankName: string;
accountNumber: string;
accountName: string;
};
};
type PaymentStats = {
totalTransactions: number;
successCount: number;
failedCount: number;
totalRevenue: number;
totalNetRevenue: number;
averageAmount: number;
};
type PaymentError = {
code: string;
message: string;
};Email provisioning types
type EmailTier = "tier-1-nexa" | "tier-2-stalwart" | "tier-3-google";
type EmailCreateOptions = {
studentId: string;
firstName: string;
lastName: string;
email?: string; // Generated from name if omitted
gradeLevel?: string; // e.g. "SS2" — used for Google Workspace OU
recoveryEmail?: string;
};
type EmailCreateResult = {
email: string;
temporaryPassword: string;
providerUserId: string;
tier: EmailTier;
};
type StudentEmailAccount = {
email: string;
studentId: string;
firstName: string;
lastName: string;
status: "active" | "suspended" | "deleted";
tier: EmailTier;
provider: "nexa" | "stalwart" | "google";
providerUserId: string;
gradeLevel?: string;
passwordResetRequired: boolean;
createdAt: number;
updatedAt: number;
};
type EmailBulkCreateOptions = {
students: EmailCreateOptions[];
};
type EmailBulkCreateResult = {
jobId: string;
tier: EmailTier;
totalStudents: number;
message: string;
};
type EmailJobStatus = {
jobId: string;
status: "pending" | "processing" | "completed" | "failed";
type: string;
totalStudents: number;
processedCount: number;
successCount: number;
failedCount: number;
results?: Array<{
studentId: string;
email?: string;
status: "created" | "failed";
error?: string;
}>;
error?: string;
createdAt: number;
startedAt?: number;
completedAt?: number;
};Webhook types
All variants discriminate on event, not type.
type WebhookFileCompleteEvent = {
event: "file.completed";
fileId: string;
userId: string;
tenantId: string;
timestamp: string;
};
type WebhookPaymentEvent = {
event: "payment.completed";
reference: string;
tenantId: string;
status: "success" | "pending" | "failed" | "abandoned";
amount: number; // smallest currency unit (kobo for NGN)
customerEmail: string;
amountPaid?: number;
fees?: number;
platformFee?: number;
netAmount?: number;
paidAt?: string;
channel?: string;
failureReason?: string;
createdAt: number; // Unix ms
updatedAt: number; // Unix ms
timestamp?: string; // ISO 8601 — webhook delivery time
};
/** Fired after a single student email account is provisioned */
type WebhookEmailCreatedEvent = {
event: "email.created";
tenantId: string;
studentId: string;
email: string;
firstName: string;
lastName: string;
gradeLevel?: string;
provider: "nexa" | "stalwart" | "google";
providerUserId: string;
tier: EmailTier;
createdAt: number; // Unix ms
};
/** Fired when an account is suspended or restored */
type WebhookEmailStatusChangedEvent = {
event: "email.status_changed";
tenantId: string;
email: string;
studentId: string;
previousStatus: "active" | "suspended";
status: "active" | "suspended";
changedAt: number; // Unix ms
};
/** Fired when a bulk email provisioning job finishes — lean counts only */
type WebhookEmailBulkCompletedEvent = {
event: "email.bulk_completed";
tenantId: string;
jobId: string;
status: "completed" | "failed";
totalStudents: number;
successCount: number;
failedCount: number;
completedAt: number; // Unix ms
};
type WebhookEvent =
| WebhookFileCompleteEvent
| WebhookPaymentEvent
| WebhookEmailCreatedEvent
| WebhookEmailStatusChangedEvent
| WebhookEmailBulkCompletedEvent;file.completed and payment.completed are auto-verified and dispatched by createRouteHandler (to onFileComplete/onPaymentComplete). The three email.* events have no built-in dispatch — verify them yourself with nexa.webhooks.verify(request) in a route you write. See Webhooks.
Analysis types
type AnalysisJob = {
jobId: string;
fileId: string;
status: "pending" | "running" | "complete" | "failed";
chunkId: string;
pageNumber: number;
createdAt: string;
completedAt: string | null;
};
type ChunkSummary = {
chunkId: string;
fileId: string;
pageStart: number;
pageEnd: number;
recordCount: number;
status: "pending" | "complete" | "failed";
};Convex integration
@nexa-ed/convex adds these schema fragments and types:
// nexaFilesSchema — spread into defineSchema()
const nexaFilesSchema = {
nexaFileResults: defineTable({
fileId: v.string(),
userId: v.string(),
tenantId: v.string(),
status: v.union(v.literal("success"), v.literal("failed")),
completedAt: v.string(),
})
.index("by_fileId", ["fileId"])
.index("by_userId", ["userId"]),
};
// nexaPaymentsSchema — spread into defineSchema()
const nexaPaymentsSchema = {
paymentTransactions: defineTable({
reference: v.string(),
tenantId: v.string(),
customerEmail: v.string(),
amount: v.number(),
status: v.string(),
// ... all PaymentTransaction fields
})
.index("by_reference", ["reference"])
.index("by_email", ["customerEmail"]),
};
// nexaStudentEmailsSchema — spread into defineSchema() when using email provisioning
const nexaStudentEmailsSchema = {
studentEmails: defineTable({
tenantId: v.string(),
studentId: v.string(),
email: v.string(),
firstName: v.string(),
lastName: v.string(),
provider: v.optional(v.union(v.literal("nexa"), v.literal("stalwart"), v.literal("google"))),
tier: v.optional(v.union(v.literal("tier-1-nexa"), v.literal("tier-2-stalwart"), v.literal("tier-3-google"))),
status: v.union(v.literal("active"), v.literal("suspended"), v.literal("deleted")),
passwordResetRequired: v.optional(v.boolean()),
createdAt: v.number(),
updatedAt: v.number(),
})
.index("by_email", ["email"])
.index("by_tenant", ["tenantId"])
.index("by_student", ["tenantId", "studentId"])
.index("by_status", ["tenantId", "status"]),
};