Publishing and access
Publishing puts a version of the content into circulation for learners; access decides who gets in and under what conditions. In Cursiva, each piece of content is a single sellable unit, and these two decisions — publishing and granting access — live separately from the editorial body.
How publishing works
Every piece of content is a single row in the database, edited in place. It holds two documents:
| Document | Role |
|---|---|
content | The editable draft — what the team sees in the editor. |
publishedContent | The snapshot the learner reads — a copy of content, written by Publish. |
While you edit, the learner sees nothing new. When you Publish, Cursiva copies the current draft into publishedContent, stamps publishedAt, and recomputes the facts derived from publishing (the reference edges and the stable sectionIds). Before the first publish, publishedContent is null: the content exists, but no one can read it or enroll.
What Publish does, step by step
- Verifies that you have edit rights in the content domain.
- Recomputes the reference edges from the document being published.
- Under a per-organization advisory lock (held until the transaction commits), writes the snapshot, the publication date, the valid edges, and the
sectionIds.
The per-organization lock serializes simultaneous publishes within the same organization. Without it, two pieces of content publishing at the same time could each pass the cycle check against the other's previous state and, on write, form a cycle. Under the lock, the second publish only proceeds after the first has committed its edges.
Rules applied to references on publish
The reference graph is computed over the published content. When publishing, Cursiva keeps only the edges whose target is safe and makes them inert otherwise:
| Edge dropped | Reason |
|---|---|
| Target in another organization | Prevents bypassing the paywall by reading another organization's paid/unlisted content for free. |
| Target with no published version | Avoids an inert card that would make the course permanently uncompletable. |
| Deleted target | The target no longer exists. |
| Reference that would create a cycle | Keeps the graph a DAG within the organization. |
| Self-reference (to itself) | Removed before the other checks. |
A dropped edge does not break the page: the referenced module's card appears inert, with no link, instead of taking the learner to a 404.
Visibility: listed vs. unlisted
Publishing makes content readable by link. Discovery is a separate decision, controlled by the Listed field.
| State | In the catalog | By direct link | Typical use |
|---|---|---|---|
| Published and listed | Yes — appears in the marketplace and the organization's catalog | Yes | Main course, sellable on its own. |
| Published and unlisted | Does not appear as a card | Yes | A module that only exists inside a course, or an offer sellable by link only. |
The public catalog lists only content that is published AND listed, newest to oldest. Modules that only make sense inside a course stay unlisted and never appear as standalone cards — but they remain sellable by direct link when they have a price.
Toggling visibility sits on the boundary between catalog and finance: both the content role and the finance role can turn it on or off.
The access rule
Access is a property of the sellable unit — the content's price — and not of the edges between pieces of content. Course composition is bundling: the parent is what gets sold; monetization is not a concern of each edge.
The core rule is short: free content is readable; priced content shows a sales preview until purchase.
| Access scenario | What the learner sees |
|---|---|
| Free content, not enrolled | The full body, readable without enrolling. Enrolling only starts recording progress. |
| Free content, enrolled | The full body, with progress, drip release, and course navigation. |
| Priced content, not purchased | The sales preview: header, marketing preamble, and the module cards. Section content is withheld. |
| Priced content, already purchased | The full body, like an enrolled learner. |
| Free child of a paid parent | Readable on its own page — it works as a sample of the paid course (the sample funnel). |
| Module of a course you are enrolled in | The nested course view, under the single root enrollment, respecting drip release. |
| Refunded / delinquent enrollment | No access — the row is kept for history and reactivation, but grants no reading. |
The sales preview
For priced content not yet purchased, Cursiva assembles the public sales view: the header, the preamble (marketing content before the first step), and the module cards stay visible, so the curriculum shows up. Section content is withheld — it belongs to the course. When something has been withheld, the preview is marked as gated and an enrollment prompt invites the visitor in.
Sample: free child of a paid parent
Because access follows each piece of content's price, a free module referenced by a paid course is fully readable on its own page. This is intentional: that free child works as a sample of the paid course. A visitor who lands on a module segment under a course they are not enrolled in is taken to the module's canonical page — where a free child is read in full and a priced child shows its own sales preview.
Composition = bundling
Referencing content assembles a course from existing pieces. Enrollment happens once, at the root (the sellable unit): there is no separate sign-up per sub-content, at any nesting depth. The learner navigates continuously through the tree under that single enrollment. Access to any module reachable through the root's published tree follows from the root enrollment — never from buying each module in isolation.
Drip release: drip and prerequisites
Within a course, each step has a release rule. The rule lives on the edge (owned by the parent document), so the same child content is paced independently in each course. Two mechanics combine:
- Drip (time delay) — releases the step after an interval. The delay is measured in hours (
afterHours) and counts from an anchor. - Prerequisites — explicit dependencies: the step only opens when the required steps are completed.
Drip anchors
| Anchor | Counts from |
|---|---|
enroll (default) | The learner's enrollment date. |
prev | The completion of the immediately preceding step in the path ("after the last section"). |
A step anchored on prev whose predecessor is not yet completed has no anchor — it stays locked, and the timer only starts when the predecessor is completed.
In temporal courses (with dated cohorts), drip anchors on the learner's cohort start, not the enrollment date — the whole cohort is released together. When the learner has no cohort, the anchor falls back to the enrollment.
How sections and modules differ
The availability evaluator applies per-step-type semantics:
| Step type | Release rule |
|---|---|
| Section (title/heading) | Prerequisites. requires: null defaults to the immediately preceding step (sequential); [] = always open; [ids] = all listed ones completed. |
| Module (reference) | The edge's rule: drip (afterHours, anchored on enroll or prev) plus explicit prerequisites. A module is never locked by mere order in the document. |
A prerequisite id that no longer exists in the content is treated as satisfied (fail-open), so a removed or stale target never locks the learner out forever. Prerequisites are kept acyclic: the "Release after" selector in the editor refuses a choice that would create a cycle.
What the learner perceives
Each step of the path carries a state: available (the rule was satisfied), locked, the lock reason (drip or prerequisite) and, for drip, the unlockAt instant when it opens. The reader delivers only the released steps — the content of locked steps is never sent to the client, so there is no "reading ahead." Steps locked by drip show a live countdown until unlock.
Enrollment, approval, and seats
Publishing enables enrollment, but the way in depends on the configuration:
- Free, open entry — the learner enrolls directly (answering the intake form, if there is one).
- Free, with approval — self-signup is closed: people request to join and the team approves. Each request carries the intake answers and becomes an enrollment once approved.
- Priced — the learner pays first and lands in the queue (when there is approval); a rejection refunds automatically. Buyers skip the intake by definition — the payment is the form.
- Seat limit — when set, the page shows the remaining seats Cal-style ("3 seats left") before the click, and new enrollments are serialized so they never oversell the limit.
Enrollment is idempotent: enrolling again returns the existing row. A refunded or delinquent enrollment keeps the row (for history and reactivation on re-payment), but grants no access while it is not active.
Sharing and embedding
Once published, the content's canonical link (/{organization}/{content}) is what you share. It generates rich link cards (title, description, and cover) when pasted into WhatsApp, LinkedIn, or X — that unfurl is the creator's ad. Unlisted content stays link-only and gets noindex.
Module links stay within the main course's URL space (/{organization}/{course}/{segment}), preserving the continuity of enrollment and navigation at any depth. The Embed setting offers a way to present the content in another context; see the settings page for the embedding details.