Thanks for taking the time to complete this assessment. The goal is to understand how you think about problems and how you structure real project work. This is a small, self-contained exercise that should take around 2–3 hours. It’s completely fine if you don’t finish everything—just note any assumptions or TODOs.
We use:
- Supabase Postgres
- Supabase Edge Functions (TypeScript)
- Next.js + TypeScript
You may use your own free Supabase project.
There are four technical tasks:
- Database schema —
backend/schema.sql - RLS policies —
backend/rls_policies.sql - Edge Function —
backend/edge-functions/create-task/index.ts - Next.js page —
frontend/pages/dashboard/today.tsx
There is also a short written question about Stripe in this README.
Feel free to use Supabase/PostgreSQL docs, or any resource you normally use.
File: backend/schema.sql
Create the following tables:
leadsapplicationstasks
Each table should include standard fields:
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null,
created_at timestamptz default now(),
updated_at timestamptz default now()Additional requirements:
applications.lead_id→ FK toleads.idtasks.application_id→ FK toapplications.idtasks.typeshould only allow:call,email,reviewtasks.due_at >= tasks.created_at- Add reasonable indexes for typical queries:
- Leads:
tenant_id,owner_id,stage - Applications:
tenant_id,lead_id - Tasks:
tenant_id,due_at,status
- Leads:
File: backend/rls_policies.sql
We want:
- Counselors can see:
- Leads they own, or
- Leads assigned to any team they belong to
- Admins can see all leads belonging to their tenant
Assume the existence of:
users(id, tenant_id, role)
teams(id, tenant_id)
user_teams(user_id, team_id)
JWT contains:
user_idroletenant_id
Tasks:
- Enable RLS on
leads - Write a SELECT policy enforcing the rules above
- Write an INSERT policy that allows counselors/admins to add leads under their tenant
File: backend/edge-functions/create-task/index.ts
Write a simple POST endpoint that:
{
"application_id": "uuid",
"task_type": "call",
"due_at": "2025-01-01T12:00:00Z"
}- Validate:
task_typeiscall,email, orreviewdue_atis a valid future timestamp
- Insert a row into
tasksusing the service role key - Return:
{ "success": true, "task_id": "..." }On validation error → return 400
On internal errors → return 500
File: frontend/pages/dashboard/today.tsx
Build a small page that:
- Fetches tasks due today (status ≠ completed)
- Uses the provided Supabase client
- Displays:
- type
- application_id
- due_at
- status
- Adds a “Mark Complete” button that updates the task in Supabase
Add a section titled:
## Stripe Answer
Write 8–12 lines describing how you would implement a Stripe Checkout flow for an application fee, including:
- When you insert a
payment_requestsrow - When you call Stripe
- What you store from the checkout session
- How you handle webhooks
- How you update the application after payment succeeds
- Push your work to a public GitHub repo.
- Add your Stripe answer at the bottom of this file.
- Share the link.
Good luck.
- When the user clicks “Pay Application Fee”, I first create a payment_request record in the database with a pending status so the payment can be tracked internally.
- I then create a Stripe Checkout Session, passing the payment_request_id and application_id in metadata. Stripe returns a session URL, and the user is redirected to the hosted checkout page.
- For payment confirmation, I rely on Stripe webhooks (checkout.session.completed) instead of the success redirect, since redirects can fail or be skipped. After verifying the webhook signature, I mark the payment as completed, store the Stripe payment intent ID, and record the payment timestamp.
- Once the payment is confirmed, I update the related application’s stage (for example, to payment_received) and optionally add a timeline entry. The webhook logic is idempotent to safely handle retries.