# Creation order

> What has to exist before each call, where each id comes from, the sequence of common flows and the errors that show up when a step is skipped.

Almost everything on Memberfy depends on something created earlier: a lesson needs a module, a module needs a course, a course needs a space. Calling the API out of order doesn't break anything, but every call made too early is refused. This guide shows the right order and the id that passes from one call to the next.

## The dependency map

<div class="flow">
<div class="flow-row"><span class="flow-label">Structure and content</span><span class="flow-node">Community</span><span class="flow-arrow">→</span><span class="flow-node">Section</span><span class="flow-arrow">→</span><span class="flow-node">Space (module)</span><span class="flow-arrow">→</span><span class="flow-node">Post · Event · Image</span></div>
<div class="flow-row"><span class="flow-label">Courses</span><span class="flow-node">Courses space</span><span class="flow-arrow">→</span><span class="flow-node">Course</span><span class="flow-arrow">→</span><span class="flow-node">Module</span><span class="flow-arrow">→</span><span class="flow-node">Lesson</span></div>
<div class="flow-row"><span class="flow-label">Sales</span><span class="flow-node">Payouts approved</span><span class="flow-arrow">→</span><span class="flow-node">Sell and withdraw</span></div>
<div class="flow-row"><span class="flow-label">Subscription</span><span class="flow-node">Billing option</span><span class="flow-arrow">→</span><span class="flow-node">Plan</span><span class="flow-arrow">→</span><span class="flow-node">Plan add-ons</span><span class="flow-arrow">→</span><span class="flow-node">Downsell</span></div>
<div class="flow-row"><span class="flow-label">Access</span><span class="flow-node">Plan or product</span><span class="flow-arrow">→</span><span class="flow-node">Space access</span></div>
<div class="flow-row"><span class="flow-label">Discount</span><span class="flow-node">Products</span><span class="flow-arrow">→</span><span class="flow-node">Coupon</span></div>
</div>

Put another way:

| To create… | You first need… | And you pass |
|---|---|---|
| A space | a section | `sectionId` |
| A post, event, course, image | a space | `spaceId` |
| A course module | a course | `courseId` |
| A lesson | a module | `moduleId` |
| A plan | the billing options (`SUBSCRIPTION` products) | `productIds` |
| An add-on on the plan | the plan, and a monthly product that isn't a plan option | `addOnProductIds` |
| Access to a space by plan or product | the plan or the product | `access.subscriptionGroupIds`, `access.productIds` |
| A coupon for specific products only | the products | `applicableProducts` |
| Any sale or payout | Business Information and payout account approved | — |

Every call below carries `Authorization` and `X-CommunityId`. See [Authentication](/api/autenticacao) and [X-CommunityId](/api/x-community-id).

## Course

1. **Section** (if there isn't one yet): [`POST /api/sections`](/api/referencia/sections-spaces/post-sections) with `title` and `visibility`. Keep `data.id` as the `sectionId`.
2. **Courses space**: [`POST /api/spaces`](/api/referencia/sections-spaces/post-spaces) with `sectionId`, `title` and `module: "courses"`. Keep the `spaceId`.
3. **Course**: [`POST /api/courses`](/api/referencia/courses/post-courses) with `communityId` (the same as in the header), `spaceId`, `title`, `slug` and `level` (`beginner`, `intermediate`, `advanced`). It starts as a draft. Keep the `courseId`.
4. **Modules**: [`POST /api/courses/module`](/api/referencia/courses/post-courses-module) with `courseId` and `title`, one per module. Keep each `moduleId`.
5. **Lessons**: [`POST /api/courses/lesson`](/api/referencia/courses/post-courses-lesson) with `moduleId`, `title` and `type` (`text`, `image`, `video`, `link`).
6. **Publish**: [`PUT /api/courses/{id}`](/api/referencia/courses/put-courses-by-id) with `status: "published"`. Only a published course accepts enrollments.

To adjust it later: [`PUT`](/api/referencia/courses/put-courses-module-by-module-id) and [`DELETE /api/courses/module/{moduleId}`](/api/referencia/courses/delete-courses-module-by-module-id) rename, reorder and delete a module (along with its lessons); [`PUT`](/api/referencia/courses/put-courses-lesson-by-lesson-id) and [`DELETE /api/courses/lesson/{lessonId}`](/api/referencia/courses/delete-courses-lesson-by-lesson-id) change a lesson's title, type, duration and order, move it to another module of the same course (`moduleId`) and delete it. Deleting keeps the progress of whoever already watched it.

To sell the course, continue with a product that opens the space (see [Event](#event), steps 3 and 4, which work the same way).

## Mentoring

A cohort with its own board, sessions and monthly billing. (With [contract length](/monetizacao/duracao-do-contrato), the option in step 3 gains `commitmentMonths`.)

1. **The cohort's private space**: [`POST /api/spaces`](/api/referencia/sections-spaces/post-spaces) with `module: "feed"` and `visibility: "private"`. Keep the `spaceId`.
2. **Sessions**: [`POST /api/events`](/api/referencia/events/post-events), one per session, with `spaceId`, `title`, `slug`, `type: "online"`, `startTime` and `endTime`.
3. **Billing option**: [`POST /api/products`](/api/referencia/products/post-communities-by-community-id-products) with `type: "SUBSCRIPTION"`, `title`, `price`, `billingInterval: "MONTHLY"` and `allowedPaymentMethods`. Keep the product's `id`.
4. **Plan**: [`POST /api/subscription-groups`](/api/referencia/subscriptions/post-communities-by-community-id-subscription-groups) with `name` and `productIds: [<id from step 3>]`. Keep the plan's `id`.
5. **Open the space to the plan**: [`PUT /api/spaces/{id}`](/api/referencia/sections-spaces/put-spaces-by-id) with `access: { "subscriptionGroupIds": [<plan id>] }`.

Step 5 only works after step 4: the plan has to exist before access can refer to it.

## Event

An in-person event with a paid ticket and an announcement in the Feed.

1. **Events space**: [`POST /api/spaces`](/api/referencia/sections-spaces/post-spaces) with `module: "events"`.
2. **Event**: [`POST /api/events`](/api/referencia/events/post-events) with `spaceId`, `title`, `slug`, `type: "in_person"`, `startTime`, `endTime` and the address (`street`, `number`, `city`…).
3. **Ticket**: [`POST /api/products`](/api/referencia/products/post-communities-by-community-id-products) with `type: "ONE_TIME"`, `price`, `hasStock: true` and `stockQuantity`. Then [publish it](/api/referencia/products/post-communities-by-community-id-products-by-id-publish).
4. **Open the space to buyers**: [`PUT /api/spaces/{id}`](/api/referencia/sections-spaces/put-spaces-by-id) with `visibility: "private"` and `access: { "productIds": [<ticket id>] }`.
5. **Pinned announcement**: [`POST /api/content`](/api/referencia/content/post-content) in the Feed space, then [`PUT /api/feed/{type}/{id}/pin`](/api/referencia/feed/put-feed-by-type-by-id-pin) with the post's id.

## Plan with add-ons

1. **The plan's billing options**: one [`POST .../products`](/api/referencia/products/post-communities-by-community-id-products) per option (Monthly, Annual), `type: "SUBSCRIPTION"`.
2. **The add-on**: another `POST .../products`, `type: "SUBSCRIPTION"`, `billingInterval: "MONTHLY"`. It does **not** go into any plan's `productIds`.
3. **Plan**: [`POST .../subscription-groups`](/api/referencia/subscriptions/post-communities-by-community-id-subscription-groups) with the `productIds` from step 1.
4. **Add-ons on the plan**: [`PUT .../subscription-groups/{id}`](/api/referencia/subscriptions/put-communities-by-community-id-subscription-groups-by-id) with `addOnProductIds: [<id from step 2>]`.

## Before selling: payouts

An order that applies to any sale:

1. [`POST .../business-information`](/api/referencia/business/post-communities-by-community-id-business-information) and [`.../submit`](/api/referencia/business/post-communities-by-community-id-business-information-submit).
2. [`POST .../payout-settings`](/api/referencia/payouts/post-communities-by-community-id-payout-settings) and [`.../submit`](/api/referencia/payouts/post-communities-by-community-id-payout-settings-submit).
3. Wait for both approvals (up to 7 business days). [`GET .../payout-settings/prerequisites`](/api/referencia/payouts/get-communities-by-community-id-payout-settings-prerequisites) tells you what's missing.

Products, billing options, plans, add-ons, coupons and downsell offers can be created and edited before approval: the product starts as `DRAFT`, in the currency of the Business Information's country (in any status), or `BRL` without it. Publishing ([`POST .../products/{id}/publish`](/api/referencia/products/post-communities-by-community-id-products-by-id-publish), or `PUT .../products/{id}` with `status: ACTIVE`), checkout and withdrawals wait for approval. An approved Business Information that gets edited goes back to `PENDING` and needs `.../submit` again; until it's approved again, checkout refuses.

## The errors you get when you skip a step

| Call | What was missing | Response |
|---|---|---|
| `POST /api/spaces` | the section | `400` · *Seção não encontrada ou não pertence a esta comunidade.* (section not found or not in this community) |
| `POST /api/spaces` (or `PUT`) | the section is more restricted | `400` · *This space cannot be more open than the section "…", which is …* |
| `PUT /api/spaces/{id}` with `access` | the plan, product or group referred to | `400` · *The access grant "…" does not exist in this community.* (`param: access`) |
| `POST /api/courses` | the space | `400` · *ID do espaço é obrigatório.* (space ID is required) or *Espaço não encontrado ou não pertence a esta comunidade.* (space not found or not in this community) |
| `POST /api/courses/module` | the course | `404` · *Curso não encontrado.* (course not found) |
| `POST /api/courses/lesson` | the module | `404` · *Module not found* |
| `POST .../subscription-groups` | the billing options | `400` · *Product not found* |
| `PUT .../subscription-groups/{id}` with `addOnProductIds` | the add-on's product, or it's already a plan option | `400` · *One of the add-ons does not exist in this community or was deleted.* / *A product that is a plan's billing option cannot be another plan's add-on.* |
| Checkout configuration | approved payouts | `400` · *Payment configuration has not been set up for this community* |
| `POST .../products/{id}/publish` (or `PUT` with `status: ACTIVE`) | approved Business Information | `403` · *To publish and start selling, the community needs approved business information…* |
| `POST .../products/{id}/publish` | the product in the approved country's currency | `400` · *This product is priced in …, but the approved business information uses …* (`param: currency`) |
| `POST /api/checkout` | approved Business Information | `403` · *Community must have approved business information to enable paid products* |
| `POST .../payouts` | balance for the amount plus the fee | `400` · *Insufficient balance. Available: …* |
| Any call | the header | `400` · *X-CommunityId é obrigatório* (X-CommunityId is required) |

Some format validations still answer in English regardless of language (like *Valid course ID is required* when the id isn't a UUID).

## Tips

- **Keep every id** that comes back in `data.id`: it's what the next call asks for.
- **Send the same `communityId`** in the body (when the route asks for it) and in the header.
- **Repeating doesn't undo**: if a sequence stops halfway, continue from the step that failed instead of starting over (starting over creates duplicates).
- In the [MCP](/mcp/ferramentas), the composite tools will follow this order on their own.
