Authoring Components
Directory structure
Section titled “Directory structure”A component is a Git repository (or subdirectory) with this layout:
my-component/ warmhub/ component.json # Identity manifest.json # Resource declarationsThe warmhub/ directory is required. If your component reacts to subscriptions, the webhook handlers themselves live wherever you deploy them; the manifest only stores their URLs.
Step 1: Create component.json
Section titled “Step 1: Create component.json”{ "id": "com.example.MyComponent", "name": "my-component", "version": "1.0.0", "description": "Watches for Foo things and processes them"}The id uses reverse-DNS format: lowercase domain segments followed by PascalCase name segments. This is the ownership key — it must be globally unique.
Step 2: Create manifest.json
Section titled “Step 2: Create manifest.json”Start with the skeleton and add sections as needed:
{ "$schema": "https://warmhub.dev/schema/component-manifest.v1.json", "component": { "id": "com.example.MyComponent", "name": "my-component", "version": "1.0.0" }, "shapes": [], "credentials": [], "subscriptions": [], "seeds": [], "health": {}, "teardown": {}}The component section must match component.json. The $schema field enables IDE autocomplete.
Add shapes
Section titled “Add shapes”Declare shapes for data your component creates or consumes:
"shapes": [ { "name": "FooInput", "fields": { "url": "string", "priority": "number" } }, { "name": "FooResult", "fields": { "summary": "string", "score": "number" } }]If your component only reacts to shapes that already exist in the target repo (created by another component or manually), you don’t need to declare them here.
Add subscriptions
Section titled “Add subscriptions”Wire webhook or cron subscriptions. Components can declare event-triggered webhook subscriptions and cron-triggered webhook subscriptions:
"subscriptions": [ { "name": "mc/on-foo-add", "trigger": { "kind": "event", "shape": "FooInput" }, "kind": "webhook", "webhookUrl": "https://handler.example.com/foo", "credentials": ["my-creds"] }]Convention: prefix subscription names with a short component abbreviation (e.g., mc/ for my-component).
Add seeds
Section titled “Add seeds”Create initial data at install time:
"seeds": [ { "kind": "thing", "shape": "ComponentConfig", "name": "my-component", "data": { "version": "1.0.0", "enabled": true } }]ComponentConfig is a built-in shape — you don’t need to declare it in shapes.
Configure health and teardown
Section titled “Configure health and teardown”"health": { "requires": { "shapes": ["FooInput", "FooResult"], "subscriptions": ["mc/on-foo-add"] }},"teardown": { "subscriptions": { "onDisable": "pause" }}wh component doctor validates the shapes, subscriptions, credentials, and seeds declared in the manifest.
Step 3: Deploy your webhook handler
Section titled “Step 3: Deploy your webhook handler”Component subscriptions post to webhook URLs that you operate; they do not run local action scripts or action-container runtimes.
Your handler should accept the webhook payload and then use the CLI, SDK, or HTTP API to write results back to WarmHub.
A minimal handler contract looks like this:
- Read
event,runId,repo, andmatchedOperationsfrom the POST body. - Start any long-running work asynchronously if needed.
- Use
callback_urlto reportprocessing,success,failure, orretry_requestedfor asynchronous work. - Authenticate write-back calls with your own token.
Step 4: Validate
Section titled “Step 4: Validate”wh component validate .Fix any errors before publishing. Common issues:
- Missing
webhookUrlon a declared subscription - Invalid trigger definitions (for example, cron without
cronspec) - Component ID format (must be reverse-DNS)
- Mismatched id/name between
component.jsonandmanifest.json
Step 5: Install and test
Section titled “Step 5: Install and test”# If your component has no subscriptions, install from a local path for developmentwh component install . --repo myorg/myrepo
# If your component declares subscriptions, install from a GitHub source insteadwh component install github.com/myorg/my-component --repo myorg/myrepo
# Set any required credentialswh credential set my-creds openai_key --value sk-...
# Check healthwh component doctor my-component --repo myorg/myrepo
# Trigger by creating datawh commit submit --add test-input --shape FooInput \ --data '{"url": "https://example.com", "priority": 1}' \ --repo myorg/myrepo
# Check subscription logswh sub log mc/on-foo-add --repo myorg/myrepo- Avoid self-triggering: If your webhook writes to the same shape the subscription monitors, it will create an infinite loop. Write to a different output shape or tighten the filter.
- Keep handlers idempotent: WarmHub retries failed deliveries. Use
X-WarmHub-Idempotency-KeyorrunIdto deduplicate side effects. - Prefix subscription names: Use a consistent prefix (e.g.,
rk/for research-knowledge) to avoid collisions with other components. - Test with
wh sub attempts: After triggering, check delivery status withwh sub attempts <runId>to see exit codes and error categories.
Hit a problem or have a question? Get in touch.