Job posting and checkout

Build public plan selection, job submission, reusable billing, invoices, and checkout.

Build the public “post a job” funnel. It can complete as a published job, a pending approval, an invoice, or a hosted checkout. The discriminated response is the source of truth.

Prerequisites

  • board.context().features.publicJobSubmission is true.
  • A route that can render plan selection and the job form.
  • HTTPS success and cancellation destinations for paid checkout.
  • A server action or protected submission handler for the final write.

The public Board API key identifies the board; it is not a payment credential.

Load plans before the form

The selected plan’s id becomes submission.selectedPlan. Show active prices and invoice behavior from the returned plan instead of maintaining a duplicate pricing table.

Submit and branch on the result

Redirect only to checkoutUrl returned by Cavuno. A return to your site is not proof that payment succeeded; the payment webhook controls publication.

Reuse existing billing

Send a verification email with sendBillingVerification({ email }), collect the token, then call getBillingOptions({ verificationToken }). A selected option maps to the top-level selectedBilling object on create; an empty options array means no existing credit.

Do not reveal billing options from an email address alone. The verification token is the authorization step.

Logo uploads accept JPEG, PNG, WebP, or GIF files up to 2 MB. fetchLogoByDomain(domain) is optional; a typed job_posting_logo_not_found error means the user should continue without an automatically discovered logo.

Expected result

Submission returns exactly one of four outcomes: a checkout URL, a published job slug, pending approval, or an invoice sent state. Validation and rejected submissions throw a typed error instead.

Verify the result

  • The route disappears when public submission is disabled.
  • Plan IDs and prices come from jobPosting.plans.
  • Each result status renders a distinct completion state.
  • Checkout uses only the returned URL.
  • A completed payment eventually produces a published job through server state, not client inference.

Errors and edge cases

  • Rejected submissions throw BoardApiError; they do not return a fifth status.
  • On-site and hybrid jobs need valid office location input. Invalid geocoding must be corrected, not silently dropped.
  • Salary values require a currency and timeframe when salary range is enabled.
  • Subscription feature entitlements can constrain featured slots and active jobs.
  • Invoice plans can require billing address, tax identity, and board-defined Buyer fields. The billing identity fields are separate from the three custom-field slots.

Production cautions

  • Keep contact and invoice fields out of analytics payloads and logs.
  • Validate URLs and file size before submission for fast feedback, then show server validation exactly.
  • Make completion pages safe to reload.
  • Re-read job state after checkout or invoice completion.

Next, add candidate access purchases with Candidate paywalls.