Extend the Ichiba marketplace as a seller. This guide covers the two main integration points: market seeders that populate your listings automatically, and seller gateways that provision cloud resources when buyers make purchases.
Market Seeders
One-shot programs that pull infrastructure data from a cloud provider's API and create or update listings in the marketplace. Run them as cron jobs to keep your catalog fresh.
Seller Gateways
Persistent HTTP services you operate. When a buyer purchases a listing, Ichiba calls your gateway to provision the underlying resource and return credentials.
Sellers on Ichiba operate two systems that work in tandem. The market seeder is responsible for the supply side: it reads your available infrastructure from a cloud provider API and publishes listings to Ichiba so buyers can discover and price-compare them. The seller gateway is responsible for fulfillment: it receives provisioning requests from Ichiba when a buyer completes a purchase, creates the actual cloud resource, and returns access credentials.
Both systems authenticate to Ichiba using a Bearer token issued to your organization. The gateway additionally uses a shared secret so Ichiba can authenticate inbound calls to it.
A market seeder is a one-shot CLI program that syncs your cloud provider's catalog into the Ichiba marketplace. It runs to completion — fetching, mapping, and upserting listings — then exits. You schedule it as a cron job (hourly, daily, etc.) to keep pricing and availability current.
When to build a seeder
Build a seeder when you want to bulk-publish many listings from a structured data source (a cloud provider API, a pricing sheet, a database) rather than creating listings one by one through the API or UI.
https://api.ichiba.techA seeder follows this sequence on every run:
ICHIBA_API_TOKEN,
ICHIBA_API_URL, and
provider credentials from environment variables.
GET /api/listings with your Bearer token
and build a name → listing ID map so you can update existing entries instead of creating duplicates.
PUT /api/listings/{id}
to update it. Otherwise call POST /api/listings
to create it.
The API reference documents the full schema. Below are representative payloads for the two asset types.
Compute listing
{
"listing": {
"name": "m6i.xlarge",
"description": "General-purpose — 4 vCPU, 16 GB RAM",
"asset_type": "compute",
"price": "0.192",
"status": "active"
},
"instance_type_spec": {
"family": "general",
"vcpus": 4,
"memory_gb": "16",
"cpu_architecture": "x86_64",
"network_gbps": "12.5",
"region": "us-east-1"
},
"network_spec": {
"ipv4_included": true,
"ipv6_supported": true,
"byoip_supported": false,
"private_networking": true,
"egress_price_per_gb": "0.09"
}
}
Block storage listing
{
"listing": {
"name": "Standard Block Storage — nyc3",
"description": "SSD-backed block storage, multi-region replicated",
"asset_type": "block_storage",
"price": "0.10",
"status": "active"
},
"block_storage_spec": {
"capacity_gb": "500",
"storage_class": "standard",
"redundancy": "multi_region",
"max_iops": 10000,
"max_throughput_mbps": "500",
"region": "nyc3"
}
}
Set status to draft
if you want to review listings before making them visible to buyers.
Change to active to publish.
The following pseudocode illustrates the core seeder loop. Adapt it to your language and cloud provider SDK of choice.
Step 1 — fetch existing listings
curl https://api.ichiba.tech/api/listings \
-H "Authorization: Bearer $ICHIBA_API_TOKEN"
Build a map of name → id from the response.
You'll use this to decide whether to POST or PUT each offering.
Step 2 — upsert loop
for each offering from provider API:
payload = map_to_ichiba_payload(offering)
if existing_id = name_to_id[payload.listing.name]:
PUT /api/listings/{existing_id} body=payload
else:
POST /api/listings body=payload
Step 3 — handle errors per listing
if response.status == 201 or 200:
created_or_updated += 1
elif response.status == 422:
log("validation error", response.body)
failed += 1
else:
log("unexpected status", response.status)
failed += 1
Error handling tip
Process each listing independently — don't abort the entire run on a single failure. Log validation errors (422) with the listing name and response body so you can inspect and fix mapping issues without rerunning the full import.
Seeders are designed to be run repeatedly. Because each run is idempotent (it upserts by name), you can schedule it as often as needed without creating duplicate listings.
Build and run (Go example)
go build -o seeder . && ./seeder
Crontab entry — run daily at midnight
0 0 * * * /opt/seeder/seeder >> /var/log/seeder.log 2>&1
Docker
docker build -t my-seeder .
docker run --env-file .env my-seeder
Pass ICHIBA_API_TOKEN,
ICHIBA_API_URL,
and provider credentials via environment variables or an env file.
Never bake secrets into the image.
A seller gateway is a persistent HTTP service that you operate. It implements a small, well-defined API that Ichiba calls to provision and deprovision cloud resources on behalf of buyers. Your gateway handles the cloud-provider-specific logic; Ichiba handles billing, discovery, and the buyer experience.
Ichiba calls your gateway — not the other way around
Your gateway is passive. It waits for Ichiba to call it. When a buyer purchases
a listing, Ichiba sends a POST /tenants request to
your gateway URL. When the buyer cancels, Ichiba sends
DELETE /tenants/{id}.
You do not need to poll Ichiba — just implement the four endpoints below.
ICHIBA_GATEWAY_SECRET and registered in your Ichiba seller profileGATEWAY_ENCRYPTION_KEYAll gateways must implement these four endpoints. Ichiba will not call any others.
/health
Liveness check
No authentication required. Return 200 OK
when the service is healthy. Ichiba checks this before routing traffic to your gateway.
{ "status": "ok" }
/tenants
Provision a resource
Ichiba calls this when a buyer purchases a listing. Provision the cloud resource and return a tenant object with access credentials. Auth: Bearer shared secret.
Request body
{
"idempotency_key": "purchase_550e8400-e29b-41d4-a716-446655440000",
"listing_id": 42,
"buyer_org_id": 7,
"asset_type": "compute",
"spec": {
"vcpus": 4,
"memory_gb": "16",
"region": "us-east-1"
}
}
Response — compute (201 Created)
{
"id": "tenant_abc123",
"status": "active",
"access_details": {
"host": "ec2-203-0-113-42.compute-1.amazonaws.com",
"username": "ubuntu",
"ssh_private_key": "-----BEGIN RSA PRIVATE KEY-----\n..."
}
}
Response — block storage (201 Created)
{
"id": "tenant_def456",
"status": "active",
"access_details": {
"volume_id": "vol-0abcdef1234567890",
"region": "us-east-1",
"device": "/dev/xvdf"
}
}
Async response (202 Accepted)
{
"id": "tenant_ghi789",
"status": "provisioning",
"access_details": null
}
Return 202 if provisioning takes more than a few seconds.
Ichiba will poll GET /tenants/{id} until
status becomes active or failed.
/tenants/{id}
Retrieve tenant status
Returns the current status and access credentials for a tenant. Auth: Bearer shared secret.
Ichiba polls this endpoint while a purchase is in pending status.
{
"id": "tenant_abc123",
"status": "active",
"access_details": { ... }
}
/tenants/{id}
Deprovision a resource
Ichiba calls this when a buyer cancels a purchase. Destroy the cloud resource and mark the tenant as cancelled. Auth: Bearer shared secret. Must be idempotent — calling DELETE on an already-cancelled tenant must return 200, not an error.
{ "id": "tenant_abc123", "status": "cancelled" }
Each tenant moves through a well-defined set of states:
| Status | Description |
|---|---|
| provisioning | Resource creation in progress (async path only) |
| active | Resource ready — access_details populated |
| failed | Provisioning failed — set an error_message |
| cancelled | Resource destroyed, tenant record retained for audit |
The normal path is provisioning → active → cancelled. If creation fails, transition to failed with an error message. Retain the tenant row in your database after cancellation — delete only the cloud resource.
1. Validate the Bearer token on every request
Every inbound request (except GET /health) must carry
Authorization: Bearer {ICHIBA_GATEWAY_SECRET}.
Reject requests with a wrong or missing token with 401 Unauthorized.
curl https://your-gateway.example.com/tenants \
-X POST \
-H "Authorization: Bearer $ICHIBA_GATEWAY_SECRET" \
-H "Content-Type: application/json" \
-d '{"idempotency_key":"k1","listing_id":42,...}'
2. Implement idempotency on POST /tenants
Before creating a resource, check your database for an existing tenant with the
same idempotency_key. If found, return it immediately
without creating a new resource. Ichiba may retry the request on network failure.
existing = db.find_tenant_by_idempotency_key(params.idempotency_key)
if existing:
return 200, existing
tenant = db.create_tenant(idempotency_key=params.idempotency_key, status="provisioning")
resource = cloud.create_resource(params.spec)
tenant.update(status="active", access_details=encrypt(resource.credentials))
return 201, tenant
3. Tag all provisioned resources
Apply these tags to every cloud resource you create so you can identify and audit Ichiba-managed infrastructure:
{
"ManagedBy": "ichiba",
"IchibaListingId": "42",
"IchibaBuyerOrgId": "7",
"IchibaTenantId": "tenant_abc123"
}
4. Isolate credentials per tenant
Create a dedicated IAM user, SSH key pair, or access credential for each tenant. Do not share credentials across tenants. This ensures that cancelling one purchase does not affect another buyer's access.
5. Encrypt credentials at rest
Store all access credentials encrypted in your database using
GATEWAY_ENCRYPTION_KEY.
Decrypt only when returning them in a tenant response.
6. Make DELETE idempotent
If the tenant is already cancelled (or the cloud resource is already gone),
return 200 with the current tenant state.
Never return an error for a resource that no longer exists.
| Variable | Required | Description |
|---|---|---|
ICHIBA_GATEWAY_SECRET |
Yes | Shared secret — Ichiba sends this as the Bearer token on every inbound call |
DATABASE_URL |
Yes | PostgreSQL connection string for persisting tenant state |
GATEWAY_ENCRYPTION_KEY |
Yes | AES-256 key (base64-encoded) used to encrypt access credentials at rest |
PORT |
No | HTTP port to listen on (default: 8080) |
| Provider credentials | Yes | AWS, DigitalOcean, Hetzner, or other provider credentials needed to create resources |
Once your gateway is deployed and passes the local lifecycle test, register it in Ichiba so the platform can route purchases to it.
https://gateway.yourcompany.com).ICHIBA_GATEWAY_SECRET.GET /health to verify connectivity.Keep your gateway reachable
Ichiba will reject purchase attempts if your GET /health
endpoint is unreachable or returns a non-200 status. Monitor your gateway's uptime
and ensure TLS is valid — Ichiba does not follow redirects or accept self-signed certificates.