VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Essentials
API overview
Authentication
Event capture
Push device API
Webhooks
Webhook delivery
Webhook events
Projects & keys
Projects
API keys
Audit log
Customers & access
Persons
Products
Perks
Sandbox
Paywalls
Paywalls
Paywall locations
Components
Component registry
Assets
Experiments & flags
Experiments
Feature flags
Analytics & events
Analytics
Analytics alerts
Events
Ingest policy
VoidQL
Tracking domains
Ads
Ads
Ad accounts
Meta measurement
Payments & stores
Payment providers
Payment provider products
Purchase history imports
Payment sources
Webhooks & notifications
Webhooks
Push notifications
Push configurations
Automations
Organization & team
Organizations
Members
Invitations
Billing
Your account
Profile
Session
Notices
Connected apps
Terms of Service
Developer tools
Schema
SDK
Retrieve the registry publisher
Claim a registry namespace
List registry components
Publish a component bundle
Publish a project component
Retrieve a registry component
Update a registry component
List a registry component's versions
Retrieve a registry component version
Update a registry component version
Retrieve a version's file
Review an install
List installed components
Install a registry component
Retrieve an install
Update an install
Uninstall a registry component

Publish a component bundle

Publishes a component bundle as a new version, creating the registry component, owned by this project, on its first version.

The bundle must pass the automated checks: the definition schema, the module and its agreement with the definition, size limits, the panel module when the definition declares one, and the conformance suite. Only people who may publish paywalls in the owner project, and organization admins, can publish versions of an existing component. Publishing the same bundle again returns the existing version.

Guide: Publishing

POST
/api/v1/projects/{projectId}/registry-components/bundles

Authorization

AuthorizationBearer <token>

A project secret key: vh_sk_... for production, vh_sk_test_... for test. Keep it on your servers.

In: header

Path parameters

projectId*string

The project's id, proj_.... A secret key works only in the project it belongs to.

Request body

application/json

TypeScript definitions

Use the request body type in TypeScript.

definitionJson*string

The text of component.json, at most 64 KiB. Its id must be <namespace>/<name> in the organization's namespace and its version a semantic version.

Length1 <= length
moduleBase64*string

The component module (module.wasm), base64 encoded.

Length1 <= length
panelBase64?|

The editor panel module (panel.wasm), base64 encoded. Required exactly when the definition declares "panel": true.

icon?|

The icon the dashboard shows: a PNG, SVG or WebP image of at most 256 KiB.

thumbnails?array<>|

At most one preview image per preview state.

readme?|

Markdown documentation people read before installing, at most 128 KiB.

agentGuide?|

Markdown guidance the Voidhash agent reads before using the component, at most 32 KiB.

Examples

curl -X POST "https://api.voidhash.com/api/v1/projects/proj_tz4a98xxat96iws9zmbrgj3a/registry-components/bundles" \  -H "Authorization: Bearer vh_sk_..." \  -H "Content-Type: application/json" \  -d '{    "definitionJson": "{\\"id\\":\\"acme/plan-picker\\",\\"version\\":\\"1.3.0\\",\\"title\\":\\"Plan picker\\",\\"description\\":\\"Selectable plans.\\",\\"icon\\":{\\"kind\\":\\"builtin\\",\\"name\\":\\"component\\"},\\"category\\":\\"\\",\\"keywords\\":[\\"plans\\",\\"pricing\\"],\\"implementation\\":{\\"kind\\":\\"wasm\\",\\"abi\\":2},\\"traits\\":{\\"frame\\":{\\"resize\\":\\"width\\"},\\"appearance\\":{},\\"effects\\":{}},\\"children\\":{\\"kind\\":\\"none\\"},\\"props\\":[{\\"name\\":\\"plans\\",\\"type\\":{\\"kind\\":\\"list\\",\\"of\\":{\\"kind\\":\\"product\\"}},\\"default\\":[]},{\\"name\\":\\"accent\\",\\"type\\":{\\"kind\\":\\"color\\"},\\"default\\":\\"#7c3aed\\"}],\\"events\\":[{\\"name\\":\\"selected\\",\\"payload\\":[{\\"name\\":\\"productId\\",\\"type\\":{\\"kind\\":\\"string\\"}}]}],\\"previewStates\\":[\\"default\\"],\\"capabilities\\":[\\"commerce\\",\\"analytics\\"],\\"panel\\":false,\\"fallback\\":\\"placeholder\\",\\"defaultSize\\":{\\"width\\":\\"fill\\",\\"height\\":\\"hug\\"}}",    "moduleBase64": "AGFzbQEAAAAB...",    "icon": {      "base64": "iVBORw0KGgoAAAANSUhEUgAA...",      "contentType": "image/png"    },    "thumbnails": [      {        "base64": "iVBORw0KGgoAAAANSUhEUgAA...",        "contentType": "image/png",        "state": "default"      }    ],    "readme": "# Plan picker\\n\\nSelectable plans for a paywall. Tapping a plan selects it and emits `selected` with its product id.\\n"  }'
{  "id": "cmp_pkg_ver_e5r8t2y6u0i3o7p1a4s9d2f6",  "registryComponentId": "cmp_pkg_h2j7k4l9z1x6c3v8b5n0m4q7",  "version": "1.3.0",  "number": 5,  "status": "ready",  "statusMessage": null,  "bundleHash": "7e2a9c4f1b8d3e6a0c5f9b2d7e4a1c8f3b6d0e9a2c5f8b1d4e7a0c3f6b9d2e5a",  "contentHash": "5d9b2e7f4a1c8d3b6e0f9a2c5d8b1e4f7a0c3d6b9e2f5a8c1d4b7e0a3f6c9d2b",  "byteSize": 61204,  "capabilities": [    "commerce",    "analytics"  ],  "definition": {    "id": "acme/plan-picker",    "version": "1.3.0",    "title": "Plan picker",    "description": "Selectable plans.",    "icon": {      "kind": "builtin",      "name": "component"    },    "category": "",    "keywords": [      "plans",      "pricing"    ],    "implementation": {      "kind": "wasm",      "abi": 2    },    "traits": {      "frame": {        "resize": "width"      },      "appearance": {},      "effects": {}    },    "children": {      "kind": "none"    },    "props": [      {        "name": "plans",        "type": {          "kind": "list",          "of": {            "kind": "product"          }        },        "default": []      },      {        "name": "accent",        "type": {          "kind": "color"        },        "default": "#7c3aed"      }    ],    "events": [      {        "name": "selected",        "payload": [          {            "name": "productId",            "type": {              "kind": "string"            }          }        ]      }    ],    "previewStates": [      "default"    ],    "capabilities": [      "commerce",      "analytics"    ],    "panel": false,    "fallback": "placeholder",    "defaultSize": {      "width": "fill",      "height": "hug"    }  },  "manifest": {    "id": "acme/plan-picker",    "version": "1.3.0",    "title": "Plan picker",    "description": "Selectable plans.",    "icon": {      "kind": "builtin",      "name": "component"    },    "category": "",    "keywords": [      "plans",      "pricing"    ],    "implementation": {      "kind": "wasm",      "abi": 2    },    "traits": {      "frame": {        "resize": "width"      },      "appearance": {},      "effects": {}    },    "children": {      "kind": "none"    },    "props": [      {        "name": "plans",        "type": {          "kind": "list",          "of": {            "kind": "product"          }        },        "default": []      },      {        "name": "accent",        "type": {          "kind": "color"        },        "default": "#7c3aed"      }    ],    "events": [      {        "name": "selected",        "payload": [          {            "name": "productId",            "type": {              "kind": "string"            }          }        ]      }    ],    "previewStates": [      "default"    ],    "capabilities": [      "commerce",      "analytics"    ],    "panel": false,    "fallback": "placeholder",    "defaultSize": {      "width": "fill",      "height": "hug"    }  },  "hasPanel": false,  "hasIcon": true,  "hasReadme": true,  "hasAgentGuide": false,  "thumbnailStates": [    "default"  ],  "warnings": [],  "conformance": {    "hasPassed": true,    "checks": [      {        "name": "module",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "manifest",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "imports",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "exports",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "lifecycle",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "values",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "tree",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "determinism",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "events",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "timers",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "layout",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "actions",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "frames",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "memory",        "status": "pass",        "errors": [],        "warnings": []      },      {        "name": "golden",        "status": "skip",        "errors": [],        "warnings": []      }    ]  },  "installCount": 0,  "publishedPaywallCount": 0,  "createdBy": "user_k3m8q1z6v9c2x5b7n4w0e8r1",  "publishedAt": "2026-03-20T14:30:00.000Z"}

{  "error": {    "type": "invalid_request",    "code": "conformance_failed",    "message": "acme/plan-picker did not pass the component conformance checks. determinism: sample: equal props rendered different trees"  }}

{  "error": {    "type": "unauthenticated",    "code": "invalid_api_key",    "message": "This secret key is not valid."  }}
{  "error": {    "type": "permission_denied",    "code": "permission_denied",    "message": "Insufficient permissions."  }}
{  "error": {    "type": "not_found",    "code": "project_not_found",    "message": "There is no project proj_tz4a98xxat96iws9zmbrgj3a, or this credential cannot use it.",    "param": "projectId"  }}

{  "error": {    "type": "conflict",    "code": "publisher_required",    "message": "The organization has no component namespace yet. An organization admin must claim a namespace (for example `acme`) before components can be published."  }}

{  "error": {    "type": "internal",    "code": "internal_error",    "message": "Something went wrong on our side. Try again, and contact support with the Request-Id header if it keeps failing."  }}
{  "error": {    "type": "unavailable",    "code": "component_checks_unavailable",    "message": "The conformance checks could not run, so nothing was published. Try again in a moment."  }}

Response body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

Errors

StatustypecodeWhen
400invalid_requestconformance_failedWhen the conformance suite fails.
400invalid_requestinvalid_bundleWhen another check fails.
409conflictpublisher_requiredBefore a namespace is claimed.
409conflictversion_already_publishedWhen the version exists with other contents.
503unavailablecomponent_checks_unavailableWhen the conformance checks could not run.

It can also fail with the errors every operation shares, such as unauthenticated; see Errors.

More in Component registry

  • Retrieve the registry publisher GET
  • Claim a registry namespace PUT
  • List registry components GET
  • Publish a project component POST
  • Retrieve a registry component GET
  • Update a registry component PATCH
  • List a registry component's versions GET
  • Retrieve a registry component version GET
  • Update a registry component version PATCH
  • Retrieve a version's file GET
  • Review an install GET
  • List installed components GET
  • Install a registry component POST
  • Retrieve an install GET
  • Update an install PATCH
  • Uninstall a registry component DELETE

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousList registry componentsNextPublish a project component

On this page

AuthorizationPath parametersRequest bodyExamplesResponse bodyErrorsMore in Component registry