> ## Documentation Index
> Fetch the complete documentation index at: https://docs.custral.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Register an embed block

> Create an embed block in the provider portal, declare what it frames and reads, get it reviewed, and get it installed in a workspace.

## Overview

An **embed block** frames a page you host inside a customer's Custral workspace. Registering one takes three things from you: the **origin** Custral frames, the **URL template** it loads, and the **scopes** your page needs to read the workspace. Custral reviews that declaration, and a workspace admin accepts the scopes when they install your module.

By the end of this page your block will be in review, and you will know what a workspace does to install it.

<Info>
  You need an approved provider first. See [Become a provider](/dev/providers/become-a-provider).
</Info>

## Before you start

* **Serve the page over `https://`** from an origin you control, such as `https://embed.acme.com`.
* **Let Custral frame it.** A `Content-Security-Policy: frame-ancestors` or `X-Frame-Options` header that blocks Custral renders your block as an empty box. The portal can check this for you (step 4).
* **Implement the handshake** if you ask for scopes. Your page posts `custral:ready` and receives its token in reply. See [Embed blocks](/dev/embeds/overview).

## 1. Create the block

<Steps>
  <Step title="Open the provider portal">
    Go to [app.custral.com/provider](https://app.custral.com/provider).
  </Step>

  <Step title="Click New embed block">
    On **Overview**, or **New embed** under **Embeds**.
  </Step>

  <Step title="Name it">
    Give it a **Key** (a stable handle such as `acme.overview-panel`), a name and icon, and a description. The name and description are what workspace members see in the block picker.
  </Step>
</Steps>

The block opens on its own page with three tabs: **Block**, **Embed** and **Parameters**.

## 2. Put it in a module

A workspace installs a **module**, and installing is where the admin is shown the scopes your blocks ask for. Put every embed that requests scopes in one.

<Steps>
  <Step title="Create a module">
    In **Modules**, click **Create Module** and give it a key, a name and a description, e.g. "Acme panels". A new module starts **Private**.
  </Step>

  <Step title="Assign the block">
    Back on the block's **Block** tab, pick the module and click **Save Settings**.
  </Step>
</Steps>

Custral decides whether a module is listed. The module's page states where it stands, and there is no status control in the portal:

| Module state | Meaning                                                                                                  |
| ------------ | -------------------------------------------------------------------------------------------------------- |
| **Private**  | Not listed for workspaces. Where every new module starts.                                                |
| **Unlisted** | Not offered in the catalog. A workspace that opens your provider directly can still find and install it. |
| **Public**   | Offered in the connections catalog to every workspace.                                                   |

## 3. Declare the embed

Open the block's **Embed** tab.

| Field                        | What to enter                                                                    | Rules                                                                                 |
| ---------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| **Origin**                   | The site Custral frames, e.g. `https://embed.acme.com`                           | `https://` only. No path, query or trailing slash. Frozen once approved.              |
| **URL template**             | The page Custral loads, e.g. `https://embed.acme.com/panel?record={{record.id}}` | Must stay on the origin. Can change after approval.                                   |
| **What your panel may read** | The scopes your page's token carries                                             | Leave empty and the frame gets no token at all. Frozen once approved.                 |
| **Browser capabilities**     | Optional. `fullscreen`                                                           | Nothing is granted unless you ask. `clipboard-write` is accepted but not yet granted. |
| **Minimum height**           | Optional. Pixels                                                                 | Your page can still grow with `custral:resize`.                                       |

The template can use these placeholders. Each value is URL-encoded, and a value that isn't available becomes an empty parameter.

| Placeholder                | Resolves to                              |
| -------------------------- | ---------------------------------------- |
| `{{org.id}}`               | The workspace                            |
| `{{user.id}}`              | The viewer                               |
| `{{component.id}}`         | This block instance                      |
| `{{record.id}}`            | The open record, on a record page        |
| `{{record.<propertyKey>}}` | That property's value on the open record |

### Scopes an embed can ask for

An embed's token reaches workspace **data**, never workspace **credentials**. So three scopes can never be declared: `applications:manage` and `webhooks:manage` (either would let your page mint a permanent key) and `widget:identify` (it vouches for who a customer's visitors are).

| Scope                                            | Lets your page                              |
| ------------------------------------------------ | ------------------------------------------- |
| `records:read` / `records:write`                 | Read, or create and update, records         |
| `objects:read`                                   | Read the object and property schema         |
| `documents:read` / `documents:write`             | Read, or create and update, documents       |
| `conversations:read` / `conversations:write`     | Read, or update and reply to, conversations |
| `property_agents:read` / `property_agents:write` | Read, or manage, property auto-fill agents  |
| `tasks:read`                                     | Read tasks                                  |
| `usage:read` / `usage:write`                     | Read, or record, usage events               |

The token is accepted by `/v1`. Ask for the least you need. The installing admin sees the list, and the token is further narrowed to what the viewer can already do: a read-only member gets a token that cannot write.

## 4. Check it, then submit

<Steps>
  <Step title="Read the resolved URL">
    Once the declaration is valid, **What Custral will actually request** shows your template filled in with obviously fake ids. This is the line your reviewer reads.
  </Step>

  <Step title="Click Check my site allows framing">
    Custral requests the page and reads its framing headers. **Your site refuses to be framed** means it would render as an empty box; allow Custral as a frame ancestor and check again. **Couldn't tell** is not a refusal and does not block review.
  </Step>

  <Step title="Click Save and submit for review">
    The block's state changes to **In review**.
  </Step>
</Steps>

## Review

A Custral reviewer answers three questions about the declaration:

1. Does the **origin** belong to your provider?
2. Are the **scopes** plausible for what the block does?
3. Can the **URL template** resolve anywhere other than the declared origin?

The block's page and the portal Overview show where it stands:

| State            | Meaning                                                                                                            |
| ---------------- | ------------------------------------------------------------------------------------------------------------------ |
| **Private**      | Not submitted. Only you can see it.                                                                                |
| **In review**    | With Custral. No workspace can install it yet.                                                                     |
| **Not approved** | The reviewer's reason is shown on the **Embed** tab. Editing the declaration and saving puts it back in the queue. |
| **Unlisted**     | Approved. A workspace that installs your module can add it to a page, but it is not offered in the catalog.        |
| **Public**       | Approved, and offered in the catalog to every workspace.                                                           |

### What changes after approval

| You change                              | Result                                                                                                                                                                                                        |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Origin** or **scopes**                | The block drops back to **Private** and needs review again. It leaves the catalog until it is re-approved, and frames already open lose access on their next `/v1` call rather than when their token expires. |
| **URL template** path or query          | Nothing. The resolved URL is still pinned to the approved origin, so ordinary releases need no review.                                                                                                        |
| Name, description, capabilities, height | Nothing.                                                                                                                                                                                                      |

## How a workspace installs it

<Steps>
  <Step title="The admin opens Settings → Integrations">
    Your module is listed on the **Modules** tab once Custral has listed both your provider and the module.
  </Step>

  <Step title="They click Install Module and review your scopes">
    An **Install \<module name>** prompt lists what your blocks can read. A module whose blocks request no scopes installs straight through.
  </Step>

  <Step title="Members add your block to a page">
    It appears in the block picker alongside Custral's own blocks.
  </Step>
</Steps>

Uninstalling the module stops every token your blocks were handed at once, not when they expire.

## Troubleshooting

| Symptom                                                    | Cause                                                                                   | Fix                                                                                                                                   |
| ---------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| **Save and submit for review** is disabled                 | The declaration has problems, listed under **Not ready to submit**                      | Fix every listed problem. They are all shown at once.                                                                                 |
| "The URL template has to stay on the origin you declared." | The template's host differs from the origin, or puts a placeholder in the host          | Keep placeholders in the path or query. `https://{{org.id}}.acme.com` is refused.                                                     |
| "One of those scopes can never be granted to an embed."    | You picked a credential or identity scope                                               | Remove it. See [Scopes an embed can ask for](#scopes-an-embed-can-ask-for).                                                           |
| **Your site refuses to be framed**                         | Your server sends `X-Frame-Options` or a `frame-ancestors` policy that excludes Custral | Allow Custral's origin in `frame-ancestors`, then check again.                                                                        |
| The block went back to **Private**                         | You changed the origin or the scopes after approval                                     | Expected. It is back in review.                                                                                                       |
| A workspace can't find your module                         | Your provider or the module isn't listed yet                                            | New providers and new modules both start hidden. [Contact support](/support) when a block is approved and you are ready to be listed. |
| A workspace can't find your block                          | The module isn't installed, or the block isn't approved yet                             | Check the block's state in the portal, then ask the admin to install the module.                                                      |
| Your page gets no token                                    | The declaration has no scopes, or the page never posted `custral:ready`                 | Declare a scope, and attach your `message` listener before posting. See [The handshake](/dev/embeds/overview#the-handshake).          |
| A write returns `insufficient_scope` for some viewers      | The token is narrowed to the viewer's own access                                        | Expected. Handle it as read-only for that viewer.                                                                                     |

## Related

* [Embed blocks](/dev/embeds/overview): the handshake, the token, and reading rows
* [Become a provider](/dev/providers/become-a-provider): the portal and developer access
* [Build for other workspaces](/dev/providers/overview): applications vs providers vs embeds
* [Embed block](/blocks/embedded/embed): the version customers configure with a pasted URL


## Related topics

- [Embed](/blocks/embedded/embed.md)
- [Embed blocks](/dev/embeds/overview.md)
- [Become a provider](/dev/providers/become-a-provider.md)
- [Build for other workspaces](/dev/providers/overview.md)
- [Developer Overview](/dev/overview.md)
