Graph Service

This document (version 0.2) defines a graph service according to the terms set out in the core specification.

The graph service describes how entities can be created, queried, updated, and linked together – including the block entity, represented by the properties sent to the block.

Glossary

Block entity: an instance of data associated with the block, conforming to the block’s schema.

Entity: an instance of data, often corresponding to a thing in the world, of a particular entity type.

Entity type: a specific type of entity, with a schema defining its structure. Entities have a type, and types have a schema.

Graph: a network of entities connected by links.

Link: a connection or relation between two entities.

Linked entities: entities which are linked to the block entity, whether directly or via other entities.

Properties: the fields on a schema, what types of data are permitted as their values, and any other validation rules. Field types may be scalar (integer, string etc.), complex objects, collections (lists/arrays), or refer to another schema.

Schema: a definition of a data structure, associated with an entity type. Entities of a given type must have properties which conform to the type’s schema.

Defining blocks

Block package

Summary

Blocks using the graph service may provide a schema which describes the shape of their own properties, if any. Embedding applications should use this to constrain the properties they send to blocks. These properties are part of the block entity.

The block’s schema may be included as a file in the block package, or referred to by remote address (a URL). If the block has a schema, a path to it must be added to their block-metadata.json, under a schema key.

Blocks may also add default and examples keys to block-metadata.json to define default and example properties conforming to their schema. If more complex example data is required that involves specifying other entities and links between them, which the block will make use of, a block package may also include an example-graph.json.

Specification

Blocks

A block using the graph service MAY expand block-metadata.json to specify:

  • schema: a path to a JSON file describing the properties the block accepts as part of the block entity, using JSON Schema vocabulary. This file may be included in the block package, or hosted at a remote address. If the schema hosted at a remote address is subject to change, bear in mind that changes in the schema may invalidate other values in the block metadata (e.g. default) or the block’s functioning (e.g. if important constraints are removed).
  • default: an object, conforming to the block’s schema, representing the default data that applications should provide when creating a block, unless (a) the block can handle being provided no data when first instantiated or (b) variants is provided (see below)
  • examples: an array of objects conforming to the block's schema which provide examples of the data that can be passed to it
  • variants: an array of objects, each with a name and properties, and optionally description, icon, and examples. These objects represents different variants of the block that the user can create, where properties sets the starting properties. As a simple example, a ‘header’ block might have variants with the name ‘Heading 1’ and ‘Heading 2’, which start with { level: 1 } and { level: 2 } as properties, respectively

If schema is defined, the JSON schema file it refers to:

  • MUST specify a required array naming any properties the block will not function without (this MAY be omitted if there are no such properties)
  • SHOULD specify any further constraints or validation requirements which improve the block’s functionality
  • MAY specify an additional root-level field, configProperties, which is an array of string values which MUST be present as a key on the block schema's properties, i.e. each must name a property the block expects. This field is used to indicate which properties a block considers to be part of 'configuring' how it appears or behaves, rather than data users are creating or viewing via the blocks.

If more complex example data is required, a block’s package may also include a file at the root named example-graph.json, which MUST be a JSON object, and MAY contain one or more of:

  • entities
  • entityTypes
  • links
  • linkedAggregations

If defined, each field MUST be an array of JSON objects, which MUST conform to the structure defined for each of these resources, as specified in entity definition.

Embedding applications

Embedding applications MUST use the constraints set in the block’s schema, if defined, to constrain the data it sends under properties in the block entity, assuming the embedding application implements the blockEntity message.

Embedding applications MAY choose to display their own UI to allow users to set any values on the block entity described in configProperties, and could allow users to set values for them for all blocks of a type (rather than on a per-block instance basis)

Entity definition

Structure

Summary

An entity is a combination of identifying fields and properties.

The identifying fields are fixed and specified in this document, and appear as keys at the root of the entity data object.

The properties can be any JSON object which is valid for the entity’s type. This object is nested under a properties key on the entity data object.

Each entity has an entity type representing the type of thing that it is. In turn, an entity type has a schema which sets constraints on the permitted properties belonging to an entity.

Links represent connections or relations between entities. In the language of an entity graph, the links are the edges, and the entities are the nodes.

Linked aggregations represent a link from a block to a particular aggregation of entities, e.g. the top 10 entities of type Person, sorted by a particular property.

Specification

Entities

When embedding applications pass entities to blocks, whether the block entity or any other:

  • entities MUST be represented by an object, which contains one or more identifying fields at the top level. The object:
    • MUST contain an entityId identifying the entity, which MUST be a string.
    • MAY contain an entityTypeId identifying the entity type the entity belongs to, which MUST be a string (if defined).
  • MUST contain a properties field if the block has defined any required properties in its schema, and otherwise MAY be omitted. If present, it:
    • MUST be an object
    • MUST conform to the constraints described in the entity’s schema, if any

i.e. any entity MUST conform to the following schema:

{
  "$id": "https://blockprotocol.org/types/services/graph/entity",
  "title": "Entity",
  "type": "object",
  "properties": {
    "entityId": {
      "type": "string"
    },
    "entityTypeId": {
      "type": "string"
    },
    "properties": {
      "$ref": "https://example.com/path-to-the-entity-schema"
    }
  },
  "required": ["entityId"]
}
Entity types

Embedding applications SHOULD also provide – via the entityTypes message – an entity type for each entity they provide the block, including the block entity. Where provided, an entity type:

  • MUST be represented by an object, which:
    • MUST contain an entityTypeId identifying the type, which MUST be a string
    • MUST contain a schema, a JSON Schema object defining the constraints on the data in the entity’s properties field, which
      • MUST be of type: "object"
      • SHOULD use our JSON Schema extensions where appropriate
      • MAY otherwise contain any valid JSON Schema

i.e. entity types MUST conform to the following JSON schema:

{
  "$id": "https://blockprotocol.org/types/services/graph/entity-type",
  "title": "EntityType",
  "type": "object",
  "properties": {
    "entityTypeId": {
      "type": "string"
    },
    "schema": {
      "$ref": "https://json-schema.org/draft/2020-12/schema"
    }
  },
  "required": ["entityTypeId", "schema"]
}

Blocks SHOULD use any schemas provided in the entityTypes message to understand the constraints on user input for each entity (e.g. what type(s) of data are valid for each property).

Links

Entities MAY be connected by a link, which:

  • MUST contain:
    • sourceEntityId: the entityId of the source entity, which MUST be a string
    • destinationEntityId: the id of a single entity the link is made to, which MUST be a string
    • path: the path to the field on the source entity this link is conceptually made on, expressed as a JSON path, which MUST be a string
  • MAY contain
    • index: the position of this link in a list (for where ordering of links is important), which MUST be an integer (if defined), where 0 is the first entry in the list

i.e. links MUST conform to the following schema:

{
  "$id": "https://blockprotocol.org/types/services/graph/link",
  "title": "Link",
  "type": "object",
  "properties": {
    "sourceEntityId": {
      "type": "string"
    },
    "destinationEntityId": {
      "type": "string"
    },
    "path": {
      "type": "string"
    },
    "index": {
      "type": "integer",
      "inclusiveMinimum": 0
    }
  },
  "required": ["sourceEntityId", "destinationEntityId", "path"]
}

Where the block entity has links from it to other entities:

  • Embedding applications MUST implement a blockGraph message which represents the graph with the block entity at the root, the data for which MUST be an object, which:
    • MUST contain a linkGroups key, which contains a list of objects which group the links in the resolved graph (i.e. the edges) by entity and path. Each object:
      • MUST contain links, an array of links
      • MUST contain the sourceEntityId, identical across all entries in links
      • MUST contain the path, identical across all entries in path
    • MUST contain a linkedEntities key, which contains a list of the entities in the resolved graph (i.e. the nodes)
    • MUST contain a depth key, which indicates the depth to which the graph around the block entity has been resolved.
  • Embedding applications MUST at least provide the links from the block entity. This represents a depth of 0. They MAY also include the entities linked from the block entity and their links which is a depth of 1, and so on.

linkGroups should conform to the following schema:

{
  "$id": "https://blockprotocol.org/types/services/graph/link-group",
  "title": "LinkGroup",
  "type": "object",
  "properties": {
    "links": {
      "type": "array",
      "items": {
        "$ref": "https://blockprotocol.org/types/services/graph/link"
      }
    },
    "sourceEntityId": {
      "type": "string"
    },
    "path": {
      "type": "string"
    }
  },
  "required": ["sourceEntityId", "destinationEntityId", "path"]
}
Linked aggregations

Blocks may also wish to link an entity to a particular aggregation of entities. To do so, blocks SHOULD create a LinkedAggregationDefinition, which:

  • MUST contain:
    • sourceEntityId: the entityId of the source entity
    • operation: an aggregation operation which the embedding application should resolve the link to, following the structure of the operation object
    • path: the path to the field on the source entity this link is conceptually made on, expressed as a JSON path, which MUST be a string

i.e. a linked aggregation definition must conform to the following schema:

{
  "$id": "https://blockprotocol.org/types/services/graph/linked-aggregation-definition",
  "title": "LinkedAggregationDefinition",
  "type": "object",
  "properties": {
    "sourceEntityId": {
      "type": "string"
    },
    "path": {
      "type": "string"
    },
    "operation": {
      "$ref": "https://blockprotocol.org/types/services/graph/aggregation-operation"
    }
  },
  "required": ["sourceEntityId", "operation", "path"]
}

Identification

Summary

Blocks must pass an entityId when making requests in respect of any entity.

Embedding applications should ensure this is stable and unique for any given entity.

Specification

Blocks MUST provide entityId when identifying a particular entity for the purpose of messages under the graph service.

Embedding applications MUST provide entityId for each entity it supplies the block at the root of the entity data object, as described under Structure above.

entityId SHOULD be a stable, unique reference to the entity, so that blocks may use it for comparing the identity of entities – i.e. so that blocks can assume that entities with the same entityId are the same entity, and that the same entity referenced in different places will have the same entityId wherever it appears.

JSON Schema extensions

Summary

We define additional keywords for use as part of a JSON Schema definition.

We will publish a JSON schema vocabulary and meta-schema which allows for validating these custom keywords.

We also intend to introduce a richer vocabulary for defining entities and links between them in JSON Schema, using composable property types and data types. Please see the RFC.

Specification

As well as the core and validation JSON Schema vocabularies:, the following keywords MAY be used at the root of the schema definition:

  • labelProperty: a single string, which MUST be present as a key on the schema's properties, i.e. it must name a property available on the entity. Where an entity’s schema is provided as part of the entityTypes message, and it contains a labelProperty pointing to a defined property on the entity, blocks MUST use the value for that property as a display name for the entity where appropriate.
{
  "$id": "https://example.com/Person",
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "age": { "type": "number" }
  },
  "labelProperty": "name"
}

Messages

Error definitions

Specification

The following error codes MAY appear as a value for code in an entry in the errors array sent with any message:

  • NOT_FOUND: the message relied on an entity that was not found
  • FORBIDDEN: the message relied on permissions that the user or block does not have
  • INVALID_INPUT: the message relied on previous input that was missing or otherwise invalid

Individual messages MAY specify only a subset of these codes as being valid for that message.

Message definitions

Summary

Messages under the graph service can be grouped into two functional categories:

  • values provided by the embedding application, marked as source: "embedder" and sentOnInitialization: true
  • requests made by the block during its lifetime, marked as source: "block"

Note that there is no reason why embedding applications cannot make requests, nor why blocks cannot send values on initialization, but the service does yet not specify any such messages.

The values are all sent from the embedding application to the block on initialization, and re-sent subsequently if they change. They are:

  • blockEntity: the block entity, conforming to the constraints defined in the block schema
  • entityTypes: the entity type definitions for any entities provided to the block in other messages
  • blockGraph: the entity graph with the block at its root, which contains: - linkedEntities: the entities linked from the block entity, and from those entities, and so on, resolved to a depth determined by the embedding application - linkGroups: the links from the block entity and the links from any entities in linkedEntities, grouped by source entity and link path
  • linkedAggregations: field containing the definitions and results of aggregations linked from the block’s entity - these are special objects which contain an aggregation definition (e.g. in plain English, "top 10 Cars sorted by age, descending") and its results at the time of provision
  • readonly: whether or not the block should display in ‘readonly’ mode, disabling or hiding any editing controls it offers.

The requests may be made by the block once it has been initialized, and represent operations which create, read, update or delete entities, entity types, and create links. They are named according to the pattern [action][Resource], e.g. createEntity.

As well as the formal service JSON specification, the messages can be sent and received via the @blockprotocol/graph package.

Specification

The messages available for exchange in the graph service are defined in the service's JSON definition and are also listed below for ease of reference.

createEntity
Request to create an entity
  • source: block
  • data: object
    propertytyperequireddescription
    entityTypeIdstringyesThe entityTypeId of the type of the entity to create
    propertiesobjectyesThe properties of the entity to create
    linkslink[]noLinks to create along with the entity
  • errorCodes: none
  • respondedToBy: createEntityResponse
  • sentOnInitialization: false
createEntityResponse
The response to a request to create an entity
  • source: embedder
  • data: object
    propertytyperequireddescription
    entityentityyesThe newly created entity
  • errorCodes: FORBIDDEN, INVALID_INPUT
  • respondedToBy: none
  • sentOnInitialization: false
updateEntity
Request to update an entity, with the properties to update
  • source: block
  • data: object
    propertytyperequireddescription
    entityIdstringyesThe entityId of the entity to update
    propertiesobjectyesThe new properties object to assign to the entity
  • errorCodes: none
  • respondedToBy: updateEntityResponse
  • sentOnInitialization: false
updateEntityResponse
The response to a request to update an entity
  • source: embedder
  • data: object
    propertytyperequireddescription
    entityentityyesThe updated entity
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
deleteEntity
Request to delete an entity, expecting 'true' in response if the operation succeeds.
  • source: block
  • data: object
    propertytyperequireddescription
    entityIdstringyesThe entityId of the entity to delete
  • errorCodes: none
  • respondedToBy: deleteEntityResponse
  • sentOnInitialization: false
deleteEntityResponse
The response to a request to delete an entity
  • source: embedder
  • data: boolean
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
getEntity
Request to retrieve an entity, expecting the entity in response.
  • source: block
  • data: object
    propertytyperequireddescription
    entityIdstringyesThe entityId of the entity to retrieve
  • errorCodes: none
  • respondedToBy: getEntityResponse
  • sentOnInitialization: false
getEntityResponse
The response to a request to get an entity
  • source: embedder
  • data: object
    propertytyperequireddescription
    entityentityyesThe retrieved entity
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
aggregateEntities
Request to retrieve an aggregation of entities.
  • source: block
  • data: object
    propertytyperequireddescription
    operationaggregation-operationyesThe aggregation operation to apply
  • errorCodes: none
  • respondedToBy: aggregateEntitiesResponse
  • sentOnInitialization: false
aggregateEntitiesResponse
The response to a request to retrieve an aggregation of entities
  • source: embedder
  • data: object
    propertytyperequireddescription
    operationaggregation-operationyesThe aggregation operation that was applied to generate the results, including any defaults set by the application.
    resultsentity[]yesThe entities returned by the aggregation operation
  • errorCodes: FORBIDDEN, INVALID_INPUT
  • respondedToBy: none
  • sentOnInitialization: false
createEntityType
Request to create an entity type, expecting the created entity type in response.
  • source: block
  • data: object
    propertytyperequireddescription
    schemaobjectyesThe schema of the entity type to create
  • errorCodes: none
  • respondedToBy: createEntityTypeResponse
  • sentOnInitialization: false
createEntityTypeResponse
The response to a request to create an entityType
  • source: embedder
  • data: object
    propertytyperequireddescription
    entityTypeentity-typeyesThe created entity type
  • errorCodes: FORBIDDEN, INVALID_INPUT
  • respondedToBy: none
  • sentOnInitialization: false
updateEntityType
Request to update an entityType, expecting the updated entityType in response.
  • source: block
  • data: object
    propertytyperequireddescription
    entityTypeIdstringyesThe entityTypeId of the entity to update
    schemaobjectyesThe new schema to assign to the entity type
  • errorCodes: none
  • respondedToBy: updateEntityTypeResponse
  • sentOnInitialization: false
updateEntityTypeResponse
The response to a request to update an entity type
  • source: embedder
  • data: object
    propertytyperequireddescription
    entityTypeentity-typeyesThe updated entity type
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
deleteEntityType
Request to delete an entityType, expecting a boolean in response.
  • source: block
  • data: object
    propertytyperequireddescription
    entityTypeIdstringyesThe entityTypeId of the entityType to delete
  • errorCodes: none
  • respondedToBy: deleteEntityTypeResponse
  • sentOnInitialization: false
deleteEntityTypeResponse
The response to a request to delete an entity type
  • source: embedder
  • data: boolean
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
getEntityType
Request to retrieve an entity type, expecting the entity type in response.
  • source: block
  • data: object
    propertytyperequireddescription
    entityTypeIdstringyesThe entityTypeId of the entity type to retrieve
  • errorCodes: none
  • respondedToBy: getEntityTypeResponse
  • sentOnInitialization: false
getEntityTypeResponse
The response to a request to get an entity type
  • source: embedder
  • data: object
    propertytyperequireddescription
    entityTypeentity-typeyesThe retrieved entity type
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
aggregateEntityTypes
Request to retrieve an aggregation of entity types.
  • source: block
  • data: object
    propertytyperequireddescription
    operationaggregation-operationyesThe aggregation operation to apply
  • errorCodes: none
  • respondedToBy: aggregateEntityTypesResponse
  • sentOnInitialization: false
aggregateEntityTypesResponse
The response to a request to retrieve an aggregation of entity types
  • source: embedder
  • data: object
    propertytyperequireddescription
    operationaggregation-operationyesThe aggregation operation that was applied to generate the results, including any defaults set by the application.
    resultsentity-type[]yesThe entity types returned by the aggregation operation
  • errorCodes: FORBIDDEN, INVALID_INPUT
  • respondedToBy: none
  • sentOnInitialization: false
createLinkResponse
The response to a request to create a link
  • source: embedder
  • data: object
    propertytyperequireddescription
    linklinkyesThe created link
  • errorCodes: FORBIDDEN, INVALID_INPUT
  • respondedToBy: none
  • sentOnInitialization: false
updateLinkResponse
The response to a request to update a link
  • source: embedder
  • data: object
    propertytyperequireddescription
    linklinkyesThe updated link
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
deleteLinkResponse
The response to a request to delete a link
  • source: embedder
  • data: boolean
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
getLinkResponse
The response to a request to get a link
  • source: embedder
  • data: object
    propertytyperequireddescription
    linklinkyesThe retrieved link
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
createLinkedAggregation
Request to create a linked aggregation, expecting the created linked aggregation in response.
  • source: block
  • data: object
    propertytyperequireddescription
    sourceEntityIdstringyesThe entityId of the source entity of this linked aggregation
    operationaggregation-operationyesThe aggregation operation to apply
    pathstringyesThe path or field on the entity where this linked aggregation is made (e.g. 'rows')
  • errorCodes: none
  • respondedToBy: createLinkedAggregationResponse
  • sentOnInitialization: false
createLinkedAggregationResponse
The response to a request to create a linkedAggregation
  • source: embedder
  • data: object
    propertytyperequireddescription
    linkedAggregationlinked-aggregation-definitionyesThe created linked aggregation
  • errorCodes: FORBIDDEN, INVALID_INPUT
  • respondedToBy: none
  • sentOnInitialization: false
updateLinkedAggregation
Request to update a linked aggregation, expecting the updated linked aggregation in response.
  • source: block
  • data: object
    propertytyperequireddescription
    aggregationIdstringyesThe aggregationId of the linked aggregation to update
    operationaggregation-operationyesThe new aggregation operation
  • errorCodes: none
  • respondedToBy: updateLinkedAggregationResponse
  • sentOnInitialization: false
updateLinkedAggregationResponse
The response to a request to update a linked aggregation
  • source: embedder
  • data: object
    propertytyperequireddescription
    linkedAggregationlinked-aggregation-definitionyesThe updated linked aggregation definition
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
deleteLinkedAggregation
Request to delete a linked aggregation, expecting a boolean in response.
  • source: block
  • data: object
    propertytyperequireddescription
    aggregationIdstringyesThe aggregationId of the linked aggregation to delete
  • errorCodes: none
  • respondedToBy: deleteLinkedAggregationResponse
  • sentOnInitialization: false
deleteLinkedAggregationResponse
The response to a request to delete a linked aggregation
  • source: embedder
  • data: boolean
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
getLinkedAggregation
Request to retrieve a linked aggregation, expecting the linked aggregation in response.
  • source: block
  • data: object
    propertytyperequireddescription
    aggregationIdstringyesThe aggregationId of the linkedAggregation to retrieve
  • errorCodes: none
  • respondedToBy: getLinkedAggregationResponse
  • sentOnInitialization: false
getLinkedAggregationResponse
The response to a request to get a linkedAggregation
  • source: embedder
  • data: object
    propertytyperequireddescription
    linkedAggregationlinked-aggregationyesThe retrieved linked aggregation
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
uploadFile
Request to upload a file and create an entity to store metadata about it.
  • source: block
  • data: object
    propertytyperequireddescription
    filenoThe file blob, if being uploaded directly. Either 'url' or 'file' is required.
    urlstringnoThe URL to take the file from, if not being uploaded directly. Either 'url' or 'file' is required.
    mediaTypestringyesThe type of media file this is
  • errorCodes: none
  • respondedToBy: uploadFileResponse
  • sentOnInitialization: false
uploadFileResponse
The response to a request to create an entity storing metadata about an uploaded file.
  • source: embedder
  • data: object
    propertytyperequireddescription
    entityIdyesThe entityId of the entity storing metadata about the file.
    urlstringyesThe URL the file is now being served from.
    mediaTypestringyesThe type of media file this is
  • errorCodes: FORBIDDEN, INVALID_INPUT, NOT_FOUND
  • respondedToBy: none
  • sentOnInitialization: false
blockEntity
The entity associated with the block
  • source: embedder
  • data: entity
  • errorCodes: none
  • respondedToBy: none
  • sentOnInitialization: true
blockGraph
A graph of entities with the block entity at the root, resolved to the specified depth.
  • source: embedder
  • data: object
    propertytyperequireddescription
    linkedEntitiesentity[]yesThe entities linked from the block entity, and potentially entities linked from them, and so on, depending on depth.
    linkGroupslink-group[]yesThe links attached to the block or the entities provided in linkedEntities, grouped by entity and path.
    depthintegeryesThe number of links that will be followed from the block when resolving the graph.
  • errorCodes: none
  • respondedToBy: none
  • sentOnInitialization: true
entityTypes
The entity types for any entities provided to the block as part of other messages.
  • source: embedder
  • errorCodes: none
  • respondedToBy: none
  • sentOnInitialization: true
linkedAggregations
Aggregations which are linked to from the block entity.
readonly
Indicates if the block should display in 'readonly' mode, i.e. without editing controls.
  • source: embedder
  • data: boolean
  • errorCodes: none
  • respondedToBy: none
  • sentOnInitialization: true

Previous

Add blocks to your app

Anyone with an existing application who wants to embed semantically-rich, reusable blocks in their product can use the protocol. Improve your app’s utility and tap into a world of structured data with no extra effort, for free.

Learn more

Build your own blocks

Any developer can build and publish blocks to the Hub for others to use. Contribute to an open-source community building a more interoperable future web.

Read the quickstart guide
We're hiring full-stack TypeScript/React developers to build blocks full-time, and grow theBlock Protocol ecosystem.Learn more