How to build a job posting and checkout flow
Combine plan selection, billing verification, submission states, and checkout.
A
JThe posting flow is a server-driven sequence. Render available plans first, collect the plan-specific job data, then branch on the create result.
Load plans before building the form
123const { data: plans } = await board.jobPosting.plans({purpose: 'job_posting',});
Use the selected plan’s id as submission.selectedPlan. The board configuration decides which plans and billing paths are available; do not hardcode a Stripe-only flow.
Collect an optional logo
1234const uploaded = await board.jobPosting.uploadLogo(file);// or: await board.jobPosting.fetchLogoByDomain('example.com')const logoUrl = uploaded.publicUrl;
Uploads accept JPEG, PNG, WebP, or GIF files up to 2 MB. A domain lookup can fail with job_posting_logo_not_found or job_posting_logo_lookup_unavailable; let the poster continue without a logo when the form allows it.
Resolve billing when required
checkBilling reports whether an email already has board credit. When verification is needed, send a token, exchange it for billing options, and use the selected option as selectedBilling.
123456789const billing = await board.jobPosting.checkBilling({email: form.contactEmail,});if (!billing.hasActiveBilling) {await board.jobPosting.sendBillingVerification({email: form.contactEmail,});}
After the poster supplies the verification token:
123const options = await board.jobPosting.getBillingOptions({verificationToken,});
If the selected option represents a subscription, call checkSubscriptionEntitlements({ email, planId }) before offering featured placement or another active slot.
Submit and branch on the returned status
123456789101112131415161718192021222324import type { CreateJobPostingInput } from '@cavuno/board';export async function submitJob(input: CreateJobPostingInput) {const result = await board.jobPosting.create(input);switch (result.status) {case 'checkout':return { action: 'redirect' as const, url: result.checkoutUrl };case 'published':return {action: 'published' as const,jobId: result.jobId,jobSlug: result.jobSlug,};case 'pending_approval':return { action: 'pending' as const, jobId: result.jobId };case 'invoice_sent':return { action: 'invoice_sent' as const, jobId: result.jobId };default: {const unhandled: never = result;throw new Error(`Unhandled posting result: ${String(unhandled)}`);}}}
The discriminated union makes an unhandled future status visible to TypeScript when you keep the switch exhaustive. A rejected submission throws a BoardApiError instead of returning another status.
While the request is pending, disable the submit action and expose its busy state to assistive technology. Preserve the form on validation or network failure, put an error summary before the form, and focus that summary. Use visible labels and group related choices with fieldset and legend. Preview the job exactly as it will appear before the final submission, including employer identity, locations, salary, description, and apply method.
Recover from checkout safely
Persist a non-sensitive draft before leaving the site. Validate checkoutUrl before redirecting. On return, use the server’s job or billing state as authority—query parameters only explain where the visitor came from.
Verify the journey
- Exercise free, checkout, approval, and invoice outcomes that the board supports.
- Upload an invalid logo and render the typed error without losing the form.
- Double-submit once and confirm the UI does not create an unintended duplicate charge or posting.
- Return from checkout with modified query parameters and confirm the API state still controls the result.
- Test a plan that has exhausted its active or featured entitlement.
- Complete every step using only a keyboard and confirm progress, errors, and the final outcome are announced.
Production notes
- Do not assume every submission goes to Stripe.
- Persist a safe draft locally before external checkout.
- Confirm the resulting job or checkout state after return.
- Never log the complete submission, billing verification token, or checkout URL.
See the Job posting reference for every billing helper and result type.