<@ULVA73B9P> What’s the difference between block t...
# marvin-ai
g
@Marvin What’s the difference between block types, block schemas, and block documents?
m
thought for 160.9 seconds
Great question! In Prefect 3.x, these three terms describe different layers of the blocks system. Think: category → schema → saved instance. - Block Type - What it is: The category or kind of block defined by a Python block class (e.g., SlackWebhook, Secret, S3Bucket). - Key traits: Has a stable slug (e.g.,
slack-webhook
), name, description, and metadata. - Cardinality: One Block Type can have many Block Schemas and many Block Documents (indirectly). - Example: “Slack Webhook” is a Block Type. - Block Schema - What it is: The versioned validation schema for a Block Type at a specific point in time (generated from the block class fields). - Key traits: Includes field definitions and capabilities, tracked by a checksum (not semantic version). A new schema is registered whenever the block class’ fields change. - Cardinality: One Block Type → many Block Schemas. - Example: The SlackWebhook fields as of today (just
url
) vs. a future version that adds
channel
. Each produces a different schema checksum. - Block Document - What it is: A saved, configured instance of a block that stores actual values. - Key traits: Has a user-given name and data; references exactly one Block Schema (via
block_schema_id
) for validation and deserialization. - Cardinality: One Block Schema → many Block Documents. - Example: A saved Slack Webhook with your org’s webhook URL named
prod-alerts
. How they relate - BlockType → BlockSchema(s) → BlockDocument(s) - BlockDocument references a specific BlockSchema; BlockSchema references a BlockType. - Versioning: When a block class changes, a new BlockSchema (new checksum) is created. Existing Block Documents continue to reference their original schema for backwards compatibility. Quick example
Copy code
from prefect.blocks.notifications import SlackWebhook

# Create a configured instance (Block Document)
slack = SlackWebhook(url="<https://hooks.slack.com/services/XXX/YYY/ZZZ>")
slack.save("prod-alerts")  # creates a Block Document tied to the current Block Schema

# Later, load and use the same document
loaded = SlackWebhook.load("prod-alerts")
await loaded.notify("Flow succeeded!")
Useful links - Concepts overview: Blocks in Prefect 3 - API refs: - Block Types: Read Block Type - Block Schemas: Read Block Schema - Block Documents: Read Block Document If you want, I can show how schema changes impact existing documents or how to migrate documents when a block’s fields change.
g
@Marvin How can I retrieve the block schemas for a specific block type?
m
thought for 375.8 seconds
Here are three ways to list block schemas for a specific block type in Prefect 3.x. 1) Python (using PrefectClient) - Get the block type by slug to obtain its ID, then filter block schemas by that ID.
Copy code
import asyncio
from prefect.client.orchestration import get_client

async def main():
    async with get_client() as client:
        # 1) Look up the Block Type by slug (e.g., "slack-webhook")
        block_type = await client.request(
            "GET", "/block-types/{slug}", path_params={"slug": "slack-webhook"}
        )
        block_type_id = block_type["id"]

        # 2) List block schemas for that type
        #    You can add limit/offset or additional filters (e.g., capabilities) as needed
        schemas = await client.request(
            "POST",
            "/block-schemas",
            json={
                "block_schemas": {
                    "block_type_id": {"any_": [block_type_id]},
                    # optional examples:
                    # "capabilities": {"all_": ["read-path"]},
                    # "version": {"equals_": 1},
                }
            },
            params={"limit": 200, "offset": 0},
        )
        print(f"Found {len(schemas)} schemas")
        for s in schemas:
            print(s["id"], s["checksum"], s.get("capabilities"))

asyncio.run(main())
2) REST API (cURL) - 1) Get the Block Type by slug (replace the URL and auth for your environment):
Copy code
curl -s \
  -H "Authorization: Bearer $PREFECT_API_KEY" \
  "$PREFECT_API_URL/api/block-types/slack-webhook"
- 2) Use the returned
id
to list schemas for that type:
Copy code
curl -s -X POST \
  -H "Authorization: Bearer $PREFECT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "block_schemas": {
          "block_type_id": { "any_": ["<BLOCK_TYPE_ID>"] }
        }
      }' \
  "$PREFECT_API_URL/api/block-schemas?limit=200&offset=0"
Docs for these endpoints: - Block Type by slug: Read Block Type by slug - List Block Schemas (with filters): Read Block Schemas 3) CLI - To inspect a block type (helpful for the slug and high-level info):
Copy code
prefect block type inspect <slug>
- To list all block types (to find the slug):
Copy code
prefect block type ls
Note: The CLI does not currently provide a command to list all schemas for a type; use the API/Python approach above to enumerate schemas. If you tell me the block type slug you’re targeting, I can paste a ready-to-run snippet with that slug filled in.
g
@Marvin If I want to create a new block document via API, do I necessarily need both a block type and a block schema?
Can’t it automatically take the latest one based on the block type?
m
thought for 180.2 seconds
Short answer: When using the REST API directly, yes—you must provide both a
block_type_id
and a
block_schema_id
to create a block document. The server does not automatically choose the “latest” schema for a type. Why: Prefect allows multiple schemas per block type over time. To preserve compatibility, the API requires you to be explicit about which schema a document targets. Two good ways to proceed - Preferred (Python SDK): Let the block class handle it - Using the Python block class automatically registers the type/schema (if needed) and saves the document against the “current” schema for that class:
Copy code
from prefect.blocks.notifications import SlackWebhook

  slack = SlackWebhook(url="<https://hooks.slack.com/services/XXX/YYY/ZZZ>")
  slack.save("prod-alerts")  # Creates a block document with the current schema
- API-only: Resolve type → pick a schema → create the document 1) Get the block type ID by slug:
Copy code
curl -s \
    -H "Authorization: Bearer $PREFECT_API_KEY" \
    "$PREFECT_API_URL/api/block-types/slack-webhook"
2) List schemas for that type and choose one (e.g., the most recently updated):
Copy code
curl -s -X POST \
    -H "Authorization: Bearer $PREFECT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
          "block_schemas": {
            "block_type_id": { "any_": ["<BLOCK_TYPE_ID>"] }
          }
        }' \
    "$PREFECT_API_URL/api/block-schemas?limit=200&offset=0"
3) Create the block document with both IDs:
Copy code
curl -s -X POST \
    -H "Authorization: Bearer $PREFECT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
          "name": "prod-alerts",
          "data": { "url": "<https://hooks.slack.com/services/XXX/YYY/ZZZ>" },
          "block_type_id": "<BLOCK_TYPE_ID>",
          "block_schema_id": "<BLOCK_SCHEMA_ID>",
          "is_anonymous": false
        }' \
    "$PREFECT_API_URL/api/block-documents"
Python (async) example that picks the most recently updated schema
Copy code
import asyncio
from prefect.client.orchestration import get_client

async def main():
    async with get_client() as client:
        bt = await client.request("GET", "/block-types/{slug}", path_params={"slug": "slack-webhook"})
        block_type_id = bt["id"]

        schemas = await client.request(
            "POST",
            "/block-schemas",
            json={"block_schemas": {"block_type_id": {"any_": [block_type_id]}}},
            params={"limit": 200},
        )
        # Choose the most recently updated (fallback to created)
        def ts(s): return s.get("updated") or s.get("created") or ""
        latest = max(schemas, key=ts)

        created = await client.request(
            "POST",
            "/block-documents",
            json={
                "name": "prod-alerts",
                "data": {"url": "<https://hooks.slack.com/services/XXX/YYY/ZZZ>"},
                "block_type_id": block_type_id,
                "block_schema_id": latest["id"],
                "is_anonymous": False,
            },
        )
        print("Created:", created["id"])

asyncio.run(main())
Docs - Create block document: POST /block-documents - Get block type by slug: GET /block-types/{slug} - List block schemas: POST /block-schemas If you share the block type slug you’re using, I can tailor a ready-to-run snippet.
g
@Marvin How is the case handled via API when one of a block’s parameters is another block?
m
thought for 458.9 seconds
Great question. When a block field is itself another block, the REST API expects you to pass a reference to the child block inside the parent’s
data
payload — not the child’s raw data. What to send in the parent’s data - For any field that is typed as a Block, set that field to a reference object: - By ID:
Copy code
{
  "block_document_id": "<uuid>"
}
- Or by slug (type/name):
Copy code
{
  "block_document_slug": "<block-type-slug>/<block-name>"
}
End-to-end flow (API-only) 1) Ensure the child block document already exists (create it first if needed). 2) In the parent’s create call (POST /block-documents), include those reference objects in
data
for the fields that are blocks. 3) The server validates that the referenced document exists and is compatible with the parent’s schema. Example payload (parent referencing a credentials block)
Copy code
POST $PREFECT_API_URL/api/block-documents
Authorization: Bearer $PREFECT_API_KEY
Content-Type: application/json

{
  "name": "my-parent",
  "data": {
    "credentials": { "block_document_slug": "aws-credentials/prod" },
    "bucket": "my-bucket",
    "region": "us-east-1"
  },
  "block_type_id": "<PARENT_BLOCK_TYPE_ID>",
  "block_schema_id": "<PARENT_BLOCK_SCHEMA_ID>",
  "is_anonymous": false
}
Alternative: reference by ID
Copy code
"credentials": { "block_document_id": "00000000-0000-0000-0000-000000000000" }
Retrieving IDs to build references - Get a block type by slug: GET /block-types/{slug} - Get a block document by name for a type (returns the document with its ID): GET /block-types/{slug}/documents/{name} Notes and gotchas - Create children first: The API does not auto-create nested child documents when you create the parent. - Schema compatibility: The referenced child must match the field’s expected Block Type; otherwise you’ll get a validation error. - Depth: You can nest blocks multiple levels; each nested block field uses the same reference-object format. - Reading back: Responses include
data
with those reference objects, and the server maintains internal
block_document_references
metadata. - Python SDK convenience: If you construct a parent block in Python and pass actual Block instances for nested fields,
parent.save(...)
will serialize those nested blocks to these reference objects automatically. Docs - Create block document: POST /block-documents - Update block document data: PATCH /block-documents/{id}/data - Blocks concepts: Blocks in Prefect 3
g
@Marvin where is the documentation about the reference to the child block inside the parent’s
data
payload?
m
thought for 458.1 seconds
Great question — in Prefect 3.x, child blocks are stored as references inside the parent block’s
data
payload. There isn’t a single standalone page that documents the exact JSON shape, but it’s covered in a few places: - Blocks concepts overview (how blocks are saved/loaded and referenced): Blocks concepts - REST API for Block Documents (shows the
data
field and
block_document_references
): Read Block Document by ID - Custom Blocks guide (mentions composing/nesting blocks): Custom blocks What it looks like in practice - In the parent’s
data
, any field that holds another Block is stored as a reference object rather than inlined: - By ID:
{"block_document_id": "<uuid>"}
- Or by slug:
{"block_document_slug": "<block-type-slug>/<block-name>"}
- The API response also includes
block_document_references
which tracks referenced child documents. Example (illustrative):
Copy code
{
  "id": "a6d2…",
  "name": "parent",
  "block_type_id": "…",
  "block_schema_id": "…",
  "data": {
    "child": {
      "block_document_id": "9b1d…"
    },
    "other_child": {
      "block_document_slug": "s3-bucket/my-bucket-creds"
    }
  },
  "block_document_references": {
    "child": "9b1d…",
    "other_child": "…"
  }
}
If you want, I can pull a live example from your workspace (e.g., given a parent block slug) to show the exact payload you’ll see from the REST API.