Retrieve a component version
Reads one version with its build status, build log and, for Rust sources, its crate files.
Versions stay readable after their component is deleted, since published paywalls may still embed them.
Guide: Components
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.
componentId*string
The component's id, cmp_....
versionId*string
The id of the component's version, cmp_ver_....
Examples
curl -X GET "https://api.voidhash.com/api/v1/projects/proj_tz4a98xxat96iws9zmbrgj3a/components/cmp_p4z7c1x9v3b8n2m6q5w0e4r7/versions/cmp_ver_k3m8x2c7v9b4n1q6w5e0r8t2" \ -H "Authorization: Bearer vh_sk_..."{ "id": "cmp_ver_k3m8x2c7v9b4n1q6w5e0r8t2", "componentId": "cmp_p4z7c1x9v3b8n2m6q5w0e4r7", "version": 3, "sourceKind": "rust", "status": "ready", "contentHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "panelHash": null, "byteSize": 58912, "manifest": { "id": "project/plan-picker", "version": "0.1.0", "title": "Plan picker", "description": "Selectable plans.", "icon": { "kind": "builtin", "name": "component" }, "category": "Project", "keywords": [], "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" ], "panel": false, "fallback": "placeholder", "defaultSize": { "width": "fill", "height": "hug" } }, "isDeprecated": false, "deprecatedAt": null, "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": [] } ] }, "createdBy": "user_k3m8q1z6v9c2x5b7n4w0e8r1", "createdAt": "2026-03-04T16:20:00.000Z", "builtAt": "2026-03-04T16:21:12.000Z", "buildLog": " Compiling plan-picker v0.1.0 (/build)\n Finished `release` profile [optimized] target(s) in 41.27s\n", "sourceFiles": { "src/lib.rs": "#![no_std]\n\nuse voidhash_ui::prelude::*;\n\n/// Selectable plans: tapping one selects it, and the button buys it.\n#[component(\n title = \"Plan picker\",\n description = \"Selectable plans.\",\n events(selected(product_id: string)),\n capabilities(commerce),\n traits(frame(resize = \"width\"), appearance, effects),\n size(width = \"fill\", height = \"hug\")\n)]\npub fn PlanPicker(cx: Cx, plans: Vec<Product>, #[prop(default = \"#7c3aed\")] accent: Color) -> View {\n let selected = cx.state(|| plans.first().map(|plan| plan.id.clone()));\n let current = selected.get();\n vstack()\n .spacing(12)\n .children(plans.iter().map(|plan| {\n let id = plan.id.clone();\n hstack()\n .key(&plan.id)\n .child(text(&plan.title).font(Font::HEADLINE))\n .child(spacer())\n .child(text(plan.price_per_period()))\n .border(if current.as_ref() == Some(&plan.id) { 2 } else { 1 }, accent)\n .corner_radius(14)\n .on_tap(move |cx| {\n selected.set(Some(id.clone()));\n cx.emit(\"selected\", object([(\"productId\", Value::from(&id))]))\n })\n }))\n .child(button(\"Continue\").prominent().on_tap(move |cx| {\n if let Some(id) = selected.get() {\n cx.purchase(&id);\n }\n }))\n}\n" }}{ "error": { "type": "invalid_request", "code": "string", "message": "string", "param": "string" }}{ "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": "component_version_not_found", "message": "This component has no version cmp_ver_k3m8x2c7v9b4n1q6w5e0r8t2.", "param": "versionId" }}{ "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." }}Response body
application/json
application/json
application/json
application/json
application/json
application/json
Errors
| Status | type | code | When |
|---|---|---|---|
| 404 | not_found | component_version_not_found | — |
It can also fail with the errors every operation shares, such as unauthenticated; see Errors.
More in Components
- List components
GET - Upload a component module
POST - Submit a component's source
POST - Retrieve a component
GET - Update a component
PATCH - Delete a component
DELETE - List a component's versions
GET - Update a component version
PATCH - List component uses
GET
Something wrong or missing on this page? Tell us by email or on Discord.