{"openapi":"3.1.0","info":{"title":"Indie Chains API","version":"1.0.0","description":"Public discovery and authenticated member operations for Indie Chains, a reviewed collaboration network for independent founders and site operators. Indie Chains sells software access; it does not sell links or guarantee publication or SEO outcomes.","contact":{"name":"Indie Chains support","email":"hello@indiechains.com","url":"https://www.indiechains.com/contact"},"license":{"name":"Proprietary — see Terms of Service","url":"https://www.indiechains.com/terms"}},"externalDocs":{"description":"Indie Chains developer and agent documentation","url":"https://www.indiechains.com/developers"},"servers":[{"url":"https://api.indiechains.com","description":"Production API"}],"tags":[{"name":"System","description":"Service availability."},{"name":"Discovery","description":"Public site and network discovery."},{"name":"Metrics","description":"Public website metric checks."},{"name":"Membership","description":"Public membership offer information."},{"name":"Reviews","description":"Approved public member reviews."},{"name":"Authentication","description":"Account authentication and identity."}],"paths":{"/health":{"get":{"operationId":"getServiceHealth","summary":"Check API health","description":"Returns a lightweight process health signal. This endpoint does not check database readiness.","tags":["System"],"responses":{"200":{"description":"The API process is healthy.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["status","service"],"properties":{"status":{"type":"string","const":"ok"},"service":{"type":"string"}}}}}}}}},"/sites":{"get":{"operationId":"listPublicSites","summary":"List eligible public sites","description":"Returns sites eligible through active or previous paid access plus manual approval, or verified/recovering badge compliance. Supports server-side discovery filters and ordering.","tags":["Discovery"],"parameters":[{"name":"category","in":"query","required":false,"description":"Return only sites in this exact category.","schema":{"type":"string","minLength":1,"maxLength":80}},{"name":"q","in":"query","required":false,"description":"Search site names, domains, slugs, categories, taglines, and descriptions.","schema":{"type":"string","maxLength":120}},{"name":"minDr","in":"query","required":false,"description":"Return only sites at or above this Domain Rating.","schema":{"type":"integer","minimum":0,"maximum":100}},{"name":"opportunity","in":"query","required":false,"description":"Return sites that support a specific collaboration opportunity.","schema":{"type":"string","enum":["guest_posts","resource_mentions","product_roundups","has_blog"]}},{"name":"sort","in":"query","required":false,"description":"Order results by newest, highest Domain Rating, or name.","schema":{"type":"string","enum":["newest","dr_desc","name_asc"],"default":"newest"}},{"name":"page","in":"query","required":false,"description":"One-based result page.","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","required":false,"description":"Maximum sites returned per page.","schema":{"type":"integer","minimum":1,"maximum":100,"default":24}}],"responses":{"200":{"description":"A page of eligible public sites.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["sites","pagination","categories"],"properties":{"sites":{"type":"array","items":{"type":"object","description":"A public site eligible through manual review or verified badge compliance.","required":["id","slug","name","url","category"],"properties":{"id":{"type":"string","description":"Stable Indie Chains record identifier."},"slug":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"name":{"type":"string"},"url":{"type":"string","format":"uri"},"tagline":{"type":["string","null"]},"category":{"type":"string"},"domainRating":{"type":["number","null"],"minimum":0,"maximum":100},"logoUrl":{"type":["string","null"],"format":"uri-reference"},"listingTrustSignal":{"type":["string","null"],"enum":["manually_reviewed","banner_verified",null]},"outboundLinkPolicy":{"type":"string","enum":["follow","nofollow"],"description":"SEO policy for links from Indie Chains to the site's external URL, derived from current Premium access."},"badgeRequired":{"type":"boolean","description":"Owner-only hint indicating whether banner verification is required to publish this site."},"seoEligibleAt":{"type":["string","null"],"format":"date-time"}}}},"pagination":{"type":"object","required":["page","limit","total","hasNextPage"],"properties":{"page":{"type":"integer","minimum":1},"limit":{"type":"integer","minimum":1},"total":{"type":"integer","minimum":0},"hasNextPage":{"type":"boolean"}}},"categories":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"Invalid query parameters.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}}}},"post":{"operationId":"createSite","summary":"Create an owned site","description":"Free accounts receive a draft with pending badge compliance; premium accounts retain manual-review submission.","tags":["Discovery"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"required":["name","slug","url","category","tags","founderContactUrl"],"properties":{"name":{"type":"string","maxLength":100},"slug":{"type":"string","minLength":3,"maxLength":64},"url":{"type":"string","format":"uri","maxLength":500},"category":{"type":"string"},"tags":{"type":"array","maxItems":3,"items":{"type":"string"}},"founderContactUrl":{"type":"string","format":"uri","maxLength":500,"description":"Required public founder or operator profile URL."}}}}}},"responses":{"201":{"description":"The site was created.","content":{"application/json":{"schema":{"type":"object","required":["site"],"properties":{"site":{"type":"object","description":"A public site eligible through manual review or verified badge compliance.","required":["id","slug","name","url","category"],"properties":{"id":{"type":"string","description":"Stable Indie Chains record identifier."},"slug":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"name":{"type":"string"},"url":{"type":"string","format":"uri"},"tagline":{"type":["string","null"]},"category":{"type":"string"},"domainRating":{"type":["number","null"],"minimum":0,"maximum":100},"logoUrl":{"type":["string","null"],"format":"uri-reference"},"listingTrustSignal":{"type":["string","null"],"enum":["manually_reviewed","banner_verified",null]},"outboundLinkPolicy":{"type":"string","enum":["follow","nofollow"],"description":"SEO policy for links from Indie Chains to the site's external URL, derived from current Premium access."},"badgeRequired":{"type":"boolean","description":"Owner-only hint indicating whether banner verification is required to publish this site."},"seoEligibleAt":{"type":["string","null"],"format":"date-time"}}}}}}}},"403":{"description":"The account cannot submit a site.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}},"409":{"description":"The slug is already in use.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}}}}},"/sites/{slug}":{"get":{"operationId":"getPublicSiteBySlug","summary":"Get a public site or badge-verification placeholder","description":"Returns one currently eligible site, or a minimal noindex-ready placeholder for a free listing awaiting banner compliance. Administratively blocked and otherwise private profiles return 404.","tags":["Discovery"],"parameters":[{"name":"slug","in":"path","required":true,"description":"The stable Indie Chains site slug.","schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Public site details or a pending-verification placeholder.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","additionalProperties":false,"required":["site"],"properties":{"site":{"type":"object","description":"A public site eligible through manual review or verified badge compliance.","required":["id","slug","name","url","category"],"properties":{"id":{"type":"string","description":"Stable Indie Chains record identifier."},"slug":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"name":{"type":"string"},"url":{"type":"string","format":"uri"},"tagline":{"type":["string","null"]},"category":{"type":"string"},"domainRating":{"type":["number","null"],"minimum":0,"maximum":100},"logoUrl":{"type":["string","null"],"format":"uri-reference"},"listingTrustSignal":{"type":["string","null"],"enum":["manually_reviewed","banner_verified",null]},"outboundLinkPolicy":{"type":"string","enum":["follow","nofollow"],"description":"SEO policy for links from Indie Chains to the site's external URL, derived from current Premium access."},"badgeRequired":{"type":"boolean","description":"Owner-only hint indicating whether banner verification is required to publish this site."},"seoEligibleAt":{"type":["string","null"],"format":"date-time"}}}}},{"type":"object","additionalProperties":false,"required":["pendingListing"],"properties":{"pendingListing":{"type":"object","additionalProperties":false,"required":["name","slug"],"properties":{"name":{"type":"string"},"slug":{"type":"string"}}}}}]}}}},"404":{"description":"No public or badge-pending site has this slug.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}}}}},"/sites/domain-rating-check":{"post":{"operationId":"checkPublicDomainRating","summary":"Check a public domain's Domain Rating","description":"Normalizes a public URL and returns the latest available Ahrefs Domain Rating. This rate-limited endpoint does not guarantee that a score is available.","tags":["Metrics"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","format":"uri","maxLength":2048}}},"example":{"url":"https://example.com"}}}},"responses":{"200":{"description":"The latest available Domain Rating result.","content":{"application/json":{"schema":{"type":"object","required":["domain","url","faviconUrl","domainRating","checkedAt"],"properties":{"domain":{"type":"string","format":"hostname"},"url":{"type":"string","format":"uri"},"faviconUrl":{"type":"string","format":"uri"},"domainRating":{"type":"number","minimum":0,"maximum":100},"checkedAt":{"type":"string","format":"date-time"}}}}}},"400":{"description":"The URL is invalid or cannot be checked.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}},"429":{"description":"The rate limit has been exceeded.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}},"502":{"description":"The metrics provider did not return a score.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}}}}},"/sites/{slug}/submit-review":{"post":{"operationId":"submitOwnedSiteForReview","summary":"Submit an owned site for manual review","description":"Moves a Premium member's draft or rejected site into the manual-review queue. Repeated submissions for pending or approved sites are idempotent.","tags":["Discovery"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug of the owned site to submit.","schema":{"type":"string","minLength":1,"maxLength":128}}],"responses":{"200":{"description":"The site's current manual-review state.","content":{"application/json":{"schema":{"type":"object","required":["site"],"properties":{"site":{"type":"object","required":["id","slug","reviewStatus","rejectionReason"],"properties":{"id":{"type":"string"},"slug":{"type":"string"},"reviewStatus":{"type":"string","enum":["pending_review","approved"]},"rejectionReason":{"type":["string","null"]}}}}}}}},"403":{"description":"Premium membership or site ownership is required.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}},"404":{"description":"The site was not found.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}},"409":{"description":"The site is suspended and cannot be submitted.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}}}}},"/sites/{slug}/badge-compliance":{"get":{"operationId":"getSiteBadgeCompliance","summary":"Get owned site badge compliance","description":"Returns owner/admin-only dark and light badge options, snippets, saved URL, status, timestamps, and recovery deadline.","tags":["Discovery"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"description":"The stable slug of the owned Indie Chains site.","schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Badge compliance details.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"403":{"description":"Owner or admin access is required.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}}}}},"/sites/{slug}/badge-compliance/verify":{"post":{"operationId":"verifySiteBadgeCompliance","summary":"Verify a free-listing badge","description":"Safely fetches a same-host page, validates the unique badge image and official Indie Chains destination in the initial HTML without executing JavaScript, and updates compliance atomically.","tags":["Discovery"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"description":"The stable slug of the owned Indie Chains site to verify.","schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["verificationUrl"],"properties":{"verificationUrl":{"type":"string","format":"uri"}}}}}},"responses":{"200":{"description":"The site is badge-compliant and eligible.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"422":{"description":"The URL or badge did not pass verification. Client-only badges must be moved into static or server-rendered HTML.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}},"429":{"description":"Verification is rate limited or cooling down.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}}}}},"/billing/tiers":{"get":{"operationId":"getMembershipTiers","summary":"Get current membership offers","description":"Returns the current public membership tier, approved-site count, verified free-site count, optional lifetime offer, and the included offer details. Verify prices immediately before presenting them to a user.","tags":["Membership"],"responses":{"200":{"description":"Current public membership information.","content":{"application/json":{"schema":{"type":"object","required":["approvedSiteCount","verifiedFreeSiteCount","activeTier","tiers"],"properties":{"approvedSiteCount":{"type":"integer","minimum":0},"verifiedFreeSiteCount":{"type":"integer","minimum":0},"activeTier":{"$ref":"#/components/schemas/MembershipTier"},"tiers":{"type":"array","items":{"$ref":"#/components/schemas/MembershipTier"}},"lifetime":{"oneOf":[{"$ref":"#/components/schemas/LifetimeOffer"},{"type":"null"}]},"offer":{"type":"object","additionalProperties":true}}}}}},"503":{"description":"Membership information is temporarily unavailable.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}}}}},"/member-reviews/public":{"get":{"operationId":"listPublicMemberReviews","summary":"List approved member reviews","description":"Returns reviews that passed moderation plus the optional admin-curated featured landing review.","tags":["Reviews"],"responses":{"200":{"description":"Approved public reviews.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["reviews","featuredReview"],"properties":{"reviews":{"type":"array","items":{"type":"object","required":["id","rating","body","reviewerJourney","user"],"properties":{"id":{"type":"string","description":"Stable Indie Chains review identifier."},"rating":{"type":"integer","minimum":1,"maximum":5},"body":{"type":"string"},"destinationUrl":{"type":["string","null"],"format":"uri"},"reviewerJourney":{"type":["string","null"],"enum":["free_user","free_to_premium",null],"description":"Public journey label for reviews first submitted by never-Premium users."},"user":{"type":"object","required":["username"],"properties":{"username":{"type":"string"},"displayName":{"type":["string","null"]},"avatarUrl":{"type":["string","null"],"format":"uri-reference"}}}}}},"featuredReview":{"oneOf":[{"type":"object","additionalProperties":false,"required":["id","rating","body","reviewerName","reviewerHandle","avatarUrl","destinationUrl"],"properties":{"id":{"type":"string"},"rating":{"type":"integer","minimum":1,"maximum":5},"body":{"type":"string","maxLength":500},"reviewerName":{"type":"string"},"reviewerHandle":{"type":"string"},"avatarUrl":{"type":"string","format":"uri"},"destinationUrl":{"type":"string","format":"uri"}}},{"type":"null"}]}}}}}}}}},"/auth/register":{"post":{"operationId":"registerAccount","summary":"Register an account","description":"Creates an Indie Chains account and returns a bearer token. Do not call this operation without the user's explicit intent to create an account.","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegistrationCredentials"}}}},"responses":{"200":{"description":"Credentials were added to an existing OAuth account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResult"}}}},"201":{"description":"The account was created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResult"}}}},"400":{"description":"The registration details are invalid.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}},"409":{"description":"An account already exists for this email.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}},"429":{"description":"The rate limit has been exceeded.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}}}}},"/auth/login":{"post":{"operationId":"loginWithEmailPassword","summary":"Authenticate with email and password","description":"Authenticates an existing account and returns a bearer token. Treat the token as a secret.","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailPasswordCredentials"}}}},"responses":{"200":{"description":"Authentication succeeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResult"}}}},"400":{"description":"The credentials are malformed.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}},"401":{"description":"The credentials are invalid.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}},"429":{"description":"The rate limit has been exceeded.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}}}}},"/auth/me":{"get":{"operationId":"getAuthenticatedAccount","summary":"Get the authenticated account","description":"Returns the account associated with the supplied bearer token.","tags":["Authentication"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The authenticated account.","content":{"application/json":{"schema":{"type":"object","required":["user"],"properties":{"user":{"$ref":"#/components/schemas/User"}}}}}},"401":{"description":"A valid bearer token is required.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code."}}}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT returned by the Indie Chains login or registration operation."}},"schemas":{"MembershipTier":{"type":"object","additionalProperties":false,"required":["name","label","approvedSiteMin","monthlyPriceCents","currency"],"properties":{"name":{"type":"string"},"label":{"type":"string"},"approvedSiteMin":{"type":"integer","minimum":0},"approvedSiteMax":{"type":["integer","null"],"minimum":0},"monthlyPriceCents":{"type":"integer","minimum":0},"currency":{"type":"string","pattern":"^[A-Z]{3}$"}}},"LifetimeOffer":{"type":"object","required":["regularPriceCents","launchPriceCents","activePriceCents","currency","checkoutReady"],"properties":{"regularPriceCents":{"type":"integer","minimum":0},"launchPriceCents":{"type":"integer","minimum":0},"activePriceCents":{"type":"integer","minimum":0},"launchApprovedSiteMax":{"type":"integer","minimum":0},"currency":{"type":"string","pattern":"^[A-Z]{3}$"},"checkoutReady":{"type":"boolean"}}},"EmailPasswordCredentials":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email","maxLength":320},"password":{"type":"string","format":"password","minLength":8,"maxLength":200}}},"RegistrationCredentials":{"allOf":[{"$ref":"#/components/schemas/EmailPasswordCredentials"},{"type":"object","required":["username"],"properties":{"username":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[A-Za-z0-9_-]+$"}}}]},"AuthResult":{"type":"object","required":["token","user"],"properties":{"token":{"type":"string","description":"Bearer token. Store and transmit it securely."},"user":{"$ref":"#/components/schemas/User"}}},"User":{"type":"object","required":["id","email","username"],"properties":{"id":{"type":"string","description":"Stable Indie Chains user identifier."},"email":{"type":"string","format":"email"},"username":{"type":"string"},"displayName":{"type":["string","null"]},"avatarUrl":{"type":["string","null"],"format":"uri-reference"}}}}}}