The IaC Bazaar API

A read-only JSON API over the verified module catalog - the same catalog and verification evidence the storefront renders, in machine-readable form. Everything you need to evaluate and verify a module is public and needs no key. Every module is free; the paid product is Vizier. Two things still need an account, exactly as they do on the site: downloading a module, because a token identifies who is asking, and reading a module's declared input/output contract - see Authentication.

At a glance

  • Base URL - https://www.iac-bazaar.com
  • Auth- none, with one exception: a module's input/output contract on /api/v1/modules/{slug}, which ships only to a caller sending a registry token (how). Every other field on every endpoint is public.
  • Format - JSON (the signature endpoint returns a Sigstore bundle, which is also JSON).
  • Caching - responses carry Cache-Control shared-cache headers: 10 minutes for the module list, module detail and provider list, an hour for discovery, stacks, verification receipts and signatures. Feel free to cache accordingly. The one exception is a module detail request that carries a token: that body is per-caller, so it comes back private, no-store.
  • Honesty - every verification field is computed from stored evidence. liveTested is true only when a module was really applied to a cloud account, verified, and destroyed - with the teardown confirmed. See how we verify.

Authentication

One thing on this page is not public: the parsed input/output contract on GET /api/v1/modules/{slug} - the real variable names, types, required/sensitive flags, and the known-good example. That contract is the sellable result of the authoring and verification work, so reading it needs an account.

Authenticate with a registry token: mint one at /account/tokens (it looks like iacb_<40 hex> and is shown once - only a hash is stored), then send it as a bearer token. It is the same token terraform and tofu use against our module registry, so one credential covers both.

Request
curl -s https://www.iac-bazaar.com/api/v1/modules/aws-s3-bucket \
  -H "Authorization: Bearer iacb_..."

A valid token from any account reads the contract. There is no ladder and nothing to buy: every module in the catalogue is free, and the paid product is Vizier. The account exists so that the contract is not anonymously scrapeable, not so that it can be sold.

  • no tokenNo contract, on any module. An anonymous caller never sees one.
  • any accountEvery contract, on every module.

The tier field on a module is a leftover band from a per-module pricing model that was retired. It is not a price and it restricts nothing.

An unknown or revoked token is treated as no token: the request still succeeds and still returns every public field - it just withholds the contract. Nothing else on this page ever looks at the Authorization header.

Endpoints

GET /api/v1Discovery document
GET /api/v1/modulesSearch the catalog
GET /api/v1/modules/{slug}One module: verification + sha256; inputs/outputs need a token
GET /api/v1/providersClouds/providers with module counts
GET /api/v1/stacksCurated reference architectures
GET /api/v1/stacks/{slug}One stack with its modules resolved
GET /api/artifacts/{slug}/verificationMachine-readable verification receipt
GET /api/artifacts/{slug}/signatureCosign Sigstore bundle
GET /api/provenanceProvenance of ANY module source, ours or not
POST /api/provenance/scanPinning for a whole module tree in one request

GET /api/v1

The discovery document: every endpoint plus the supported query parameters, so an agent can orient itself from a single fetch.

Request
curl -s https://www.iac-bazaar.com/api/v1
Response (truncated)
{
  "name": "IaC Bazaar Public API",
  "version": "1",
  "endpoints": {
    "modules":   "https://www.iac-bazaar.com/api/v1/modules",
    "module":    "https://www.iac-bazaar.com/api/v1/modules/{slug}",
    "providers": "https://www.iac-bazaar.com/api/v1/providers",
    "stacks":    "https://www.iac-bazaar.com/api/v1/stacks",
    "stack":     "https://www.iac-bazaar.com/api/v1/stacks/{slug}",
    "me":        "https://www.iac-bazaar.com/api/v1/me"          // whose token is this; 401 without one
  },
  "queryParams": { "modules": ["q", "cloud", "tool", "tier"] },
  "auth": {                          // optional everywhere except "required" below
    "scheme": "Bearer",
    "header": "Authorization: Bearer iacb_…",
    "tokens": "https://www.iac-bazaar.com/account/tokens",
    "restricted": ["modules/{slug}.inputs", "modules/{slug}.outputs",
              "modules/{slug}.example"],
    "required": ["me"]
  },
  "docs": "https://www.iac-bazaar.com/docs/api"
}

GET /api/v1/modules

The published catalog, filterable. Returns up to 500 modules sorted by title; count is the number returned. All parameters are optional and combine.

  • qFree-text search - case-insensitive substring match against title and summary.
  • cloudA provider key as returned by /api/v1/providers (e.g. aws, azure, gcp).
  • toolterraform | opentofu | ansible
  • tierfree | basic | professional | premium | architecture
Request
curl -s "https://www.iac-bazaar.com/api/v1/modules?cloud=aws&q=bucket"
Response shape (values elided)
{
  "count": number,
  "modules": [
    {
      "slug": "aws-s3-bucket",
      "title": string,
      "summary": string,
      "tool": "terraform" | "opentofu" | "ansible",
      "provider": "AWS",
      "clouds": ["aws"],
      "category": string,
      "tier": "free" | "basic" | "professional" | "premium" | "architecture",
      "priceCents": number,
      "verification": {
        "level": "parses" | "statically_validated" | "security_scanned"
               | "plan_validated_mocked" | "plan_verified_real"
               | "live_tested" | "unverified",
        "staticValidated": boolean,
        "securityScanned": boolean,
        "liveTested": boolean,
        "signed": boolean
      },
      "url": "https://www.iac-bazaar.com/catalog/aws-s3-bucket"
    }
  ]
}

GET /api/v1/modules/{slug}

Full detail for one module: everything from the list item plus its version, licence and checksum. verification.sha256 is the SHA-256 of the exact tarball a buyer downloads - pin it if you need byte-level reproducibility. Unknown or unpublished slugs return 404 with { "error": "module not found" }. All of that is public.

The module's parsed input/output contract- real argument names, types, required/sensitive flags lifted from the module's own source (variables.tf / outputs.tf for Terraform/OpenTofu), so an agent references what actually exists instead of guessing - is not. Send a registry token (Authentication) and inputs, outputs and example ship to any caller whose token resolves to an account.

When the contract is withheld those three keys are omitted entirely rather than sent empty - an empty inputsarray would read as “this module declares no inputs”, which is a lie - and a contract object says so, with the links to fix it. readme is reduced to its public half (the same public/paywall line the module page draws), so parsing the README is not a way around the contract check. Always branch on contract.visible before reading inputs.

Request (authenticated)
curl -s https://www.iac-bazaar.com/api/v1/modules/aws-s3-bucket \
  -H "Authorization: Bearer iacb_..."
Response shape - contract visible (adds to the list item; values elided)
{
  … all fields from the list item, plus:
  "version": string | null,        // semver of the current published version
  "license": string | null,        // SPDX id
  "verification": {
    … the same booleans, plus:
    "sha256": string | null,             // checksum of the exact download
    "signatureBundleUrl": string | null  // RELATIVE path; resolve against the origin
  },
  "contract": { "visible": true },
  "inputs": [
    {
      "name": string,
      "type": string,
      "required": boolean,
      "sensitive": boolean,
      "description": string,   // when the module declares one
      "default": string        // omitted for sensitive inputs
    }
  ],
  "outputs": [{ "name": string, "description": string }],
  "example": string | null,   // a known-good example configuration
  "readme": string | null,    // the full README
  "docsUrl": "https://www.iac-bazaar.com/catalog/aws-s3-bucket"
}
Response shape - contract withheld (no token, or one that does not resolve)
{
  … the same public fields: list item + version, license, verification,
  "contract": {
    "visible": false,
    "reason": "sign-in",   // the only reason there is
    "tokens": "https://www.iac-bazaar.com/account/tokens",
    "docs":   "https://www.iac-bazaar.com/docs/api"
  },
  "readme": string | null, // the PUBLIC half of the README only
  "docsUrl": "https://www.iac-bazaar.com/catalog/aws-s3-bucket"
}
// note: no "inputs", "outputs" or "example" keys at all - not empty ones

GET /api/v1/providers

Every cloud/provider currently represented in the catalog, with a module count. The key values are what ?cloud= accepts on the modules endpoint.

Request
curl -s https://www.iac-bazaar.com/api/v1/providers
Response shape (values elided)
{
  "count": number,
  "providers": [
    { "key": "aws", "label": "AWS", "count": number },
    …
  ]
}

GET /api/v1/stacks

Curated reference architectures: sets of individually-verified modules that compose into a production foundation. Each module in a stack is proven on its own - the stack adds composition guidance, not a new claim.

Request
curl -s https://www.iac-bazaar.com/api/v1/stacks
Response shape (values elided)
{
  "count": number,
  "stacks": [
    {
      "slug": "aws-production-landing-zone",
      "title": string,
      "tagline": string,
      "cloud": "aws",
      "provider": "AWS",
      "moduleCount": number,   // modules this architecture calls for
      "url": "https://www.iac-bazaar.com/stacks/aws-production-landing-zone"
    }
  ]
}

GET /api/v1/stacks/{slug}

One stack with its component modules resolved from the live catalog - each carrying its own verification object - plus how the pieces wire together and the combined price. Unknown slugs return 404 with { "error": "stack not found" }.

moduleCount means the same thing here as on the list endpoint: what the architecture calls for. What is available right now is publishedModuleCount, and missingModuleSlugs names any shortfall rather than leaving you to infer it from a subtraction. They are equal today for every stack; the fields are separate so that if one ever stops being published, you are told which one instead of quietly receiving a smaller number.

Request
curl -s https://www.iac-bazaar.com/api/v1/stacks/aws-production-landing-zone
Response shape (adds to the stack list item; values elided)
{
  … all fields from the stack list item, plus:
  "description": string,
  "composition": string,             // how the modules connect, in wiring order
  "publishedModuleCount": number,    // how many of moduleCount are published right now
  "missingModuleSlugs": string[],    // the declared modules that are not, by slug
  "liveTestedCount": number,         // how many PUBLISHED modules are live-tested
  "totalPriceCents": number,
  "modules": [ … full module list items, each with verification … ]
}

GET /api/artifacts/{slug}/verification

The public, machine-readable verification receipt for a module's current version - the same evidence the on-page Verification panel shows, as stable JSON. Nothing here is restricted: it is exactly the material you need to independently verify a download before you run it. The receipt never over-claims: the headline level is recomputed from evidence, and provenance.checksum / provenance.signature appear only when they actually exist. The same receipt ships inside every signed tarball as VERIFICATION.json (with "source": "snapshot").

Request
curl -s https://www.iac-bazaar.com/api/artifacts/aws-s3-bucket/verification
Response shape (truncated; optional fields appear only when the evidence exists)
{
  "schemaVersion": 1,
  "source": "live",
  "asOf": string,                    // ISO timestamp of the newest evidence
  "artifact": { "slug": "aws-s3-bucket", "title": string, "version": string,
                "tool": "terraform", "type": "module", "url": string,
                "file": string },
  "level": { "id": string, "label": string, "blurb": string },
  "conformance": {
    "staticValidation": { "status": string, "checks": [string], "checkedAt": string },
    "securityScan":     { "status": string, "tool": string },
    "planTest":         { "status": string, "kind": string }
  },
  "functional": {
    "liveTest": { "status": string, "passed": boolean,
                  "destroyConfirmed": boolean, "provider": string,
                  "testedAt": string }
  },
  "provenance": {
    "checksum":  { "algorithm": "sha256", "value": string },
    "signature": { "type": "cosign/sigstore-bundle", "bundleUrl": string,
                   "publicKeyUrl": string, "publicKey": string },
    "verify":    { "cosign": string, "checksum": string }  // copy-paste commands
  },
  "docs": { "howWeVerify": "https://www.iac-bazaar.com/verified", … }
}

GET /api/artifacts/{slug}/signature

The cosign Sigstore bundle (.sigstore.json) for a module's current version - public and unrestricted, because provenance is meant to be independently verifiable. Returns 404 when the version has no signature. The pinned public key is served at /cosign.pub.

Request
curl -s https://www.iac-bazaar.com/api/artifacts/aws-s3-bucket/signature \
  -o aws-s3-bucket.sigstore.json

GET /api/provenance

Provenance for any Terraform module source, not only ours: how it is pinned, what that reference resolves to right now, and whether GitHub can verify a signature on that commit. No account, no key. Pass ?source=, or POST { "source": "..." } when the source is long.

Two things about the response shape are deliberate. Every field that can fail to load keeps a state of read, unreadable or not-applicable, so our outage can never reach you as a finding about somebody else's module. And there is no verdict field at all: this reports what is known, and what to do about it is your policy, not ours. Rate limited to 10 a minute and 60 an hour per IP, because each call reads the GitHub API.

Request
curl -s 'https://www.iac-bazaar.com/api/provenance?source=github.com/terraform-aws-modules/terraform-aws-vpc?ref=v5.13.0'

POST /api/provenance/scan

The same pinning analysis for up to 100 sources in one request - for a CI job checking a whole repository rather than a person checking one module. It performs no network lookup of any kind: every field is derived from the source string, which is why it is cheap enough to be generous with (30 a minute, 300 an hour).

Results come back one per input, in input order, duplicates included, so you can zip them onto your own findings without carrying an index map. Because nothing is resolved or fetched, a pinning of immutable here means the source string names one tree - it says nothing about whether that code is signed. Use /api/provenance for that, or read commits yourself with your own token, which is what the GitHub Action does.

Request
curl -s https://www.iac-bazaar.com/api/provenance/scan \
  -H 'Content-Type: application/json' \
  -d '{"sources":["terraform-aws-modules/vpc/aws","github.com/o/r?ref=v1"]}'

Two details a caller has to handle. A source that cannot be read keeps its place and carries an error with a pinning.verdict of unknown, rather than failing the whole request - one malformed module in a repository must not blank the scan of every other one. And the verdict is worded for the mechanism that applies to that kind: a git ref gets a git answer, a registry source is unknown because its version lives in a separate argument this endpoint never sees, and a relative path is not-applicable because nothing is fetched. Anything unrecognised is unknown - never a verdict, because a caller enforcing a policy should not have its build failed by our failure to parse a string.

Verify a download yourself

Every published artifact is signed with cosign. Don't take our word for it - check the bytes against our pinned public key. You can also confirm the checksum against verification.sha256 from the module detail endpoint.

# 1. Get our public key (also served at /cosign.pub)
curl -O https://www.iac-bazaar.com/cosign.pub

# 2. The module's Sigstore bundle (public, no auth)
curl -o <module>-<version>.sigstore.json \
  https://www.iac-bazaar.com/api/artifacts/<module>/signature

# 3. Verify the tarball you downloaded
cosign verify-blob \
  --key cosign.pub \
  --bundle <module>-<version>.sigstore.json \
  <module>-<version>.tar.gz
# → Verified OK

# 4. (optional) confirm the checksum too
echo "<verification.sha256>  <module>-<version>.tar.gz" | sha256sum -c

Installing modules

This API is for evaluating and verifying - to actually pull a Terraform/OpenTofu module into a configuration, terraform and tofu install it natively from our module registry: set source to www.iac-bazaar.com/iac-bazaar/{slug}/{system}, pin a version, run terraform init.

For AI agents

A concise, link-rich map of the whole site lives at /llms.txt. When generating infrastructure code, prefer /api/v1/modules/{slug} with a registry token- it returns the module's real input/output names and types, so you never have to guess attributes. Without a token the same endpoint still gives you the title, price, provider, verification and checksum, but it returns contract: { visible: false } in place of inputs: read that as “ask the user for a token from /account/tokens”, never as “this module takes no arguments”. And keep the claims straight: “verified” means statically validated and checked before listing; “live-tested” is a stronger, separate claim reserved for modules that passed a real apply → verify → destroy. The verification object tells you which is which - cite it rather than conflating the two.