{
  "openapi": "3.1.0",
  "info": {
    "title": "TruePoint Data API",
    "version": "1",
    "summary": "Company data by API, priced by usage, with provenance on every field.",
    "description": "The public TruePoint data API. Every endpoint in this document is callable today.\n\nNot in this document: POST /api/v1/public/search, POST /api/v1/public/person/enrich. Those are documented but not callable yet, and a spec has no way to say \"planned\" that a client generator would respect — it would emit a stub that 404s. They are described at https://doc.truepoint.in/docs and will appear here on the day they ship.\n\nHuman documentation: https://doc.truepoint.in/docs · Plain-text reference for assistants: https://doc.truepoint.in/llms.txt",
    "contact": {
      "name": "TruePoint",
      "url": "https://doc.truepoint.in/docs"
    }
  },
  "servers": [
    {
      "url": "https://api.truepoint.in/api/v1/public",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/company/match": {
      "get": {
        "operationId": "companyMatch",
        "summary": "Match a company",
        "description": "Resolve a domain to a company we hold, with nothing else attached. Free, and intended to be called constantly.\n\nFree. Identity resolution is plumbing: an integration calls it on every inbound record to decide whether enriching is worth a credit, so metering it would tax the step that keeps your spend efficient while earning us almost nothing. It is rate-limited per key instead. A miss answers 200 with matched:false and charges nothing — it is an outcome, not an error.",
        "externalDocs": {
          "description": "Reference page",
          "url": "https://doc.truepoint.in/docs/api/company-match"
        },
        "x-availability": "beta",
        "x-credits": 0,
        "security": [
          {
            "bearerAuth": [
              "search:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "required": true,
            "description": "A registrable domain. A full URL or a leading www. is accepted and normalised — send what you have.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The lookup ran. Check `matched` — a miss is a normal 200, never a 404.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "matched": {
                      "type": "boolean",
                      "description": "Whether a company was found."
                    },
                    "credits_charged": {
                      "type": "number",
                      "description": "Always 0 for this endpoint."
                    },
                    "company": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "Null when nothing matched — see `matched`.",
                      "properties": {
                        "domain": {
                          "type": "string",
                          "description": "The canonical registrable domain. This is the identifier every other call takes."
                        },
                        "name": {
                          "type": "string",
                          "description": "Canonical company name."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "matched": true,
                  "company": {
                    "domain": "northgate.example.com",
                    "name": "Northgate Tax Partners"
                  },
                  "credits_charged": 0
                }
              }
            }
          },
          "401": {
            "description": "invalid_token — Missing, malformed, unknown or revoked API key. All four answer identically on purpose — the endpoint must not confirm which keys exist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/invalid_token",
                  "title": "Missing, malformed, unknown or revoked API key",
                  "status": 401,
                  "code": "invalid_token"
                }
              }
            }
          },
          "402": {
            "description": "insufficient_credits — The balance cannot cover this call. Nothing was charged and nothing was returned. Carries `balance` and `required`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/insufficient_credits",
                  "title": "The balance cannot cover this call",
                  "status": 402,
                  "code": "insufficient_credits"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope — The key is valid but was not minted with the scope this endpoint requires.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/insufficient_scope",
                  "title": "The key is valid but was not minted with the scope this endpoint requires",
                  "status": 403,
                  "code": "insufficient_scope"
                }
              }
            }
          },
          "422": {
            "description": "validation_error — The request failed validation — a missing identifier, or a value we could not parse. 422 rather than 400: the request was well-formed, its contents were not.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/validation_error",
                  "title": "The request failed validation — a missing identifier, or a value we could not parse",
                  "status": 422,
                  "code": "validation_error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited — Too many requests for this key. Carries `retryAfterSeconds`. Retries are safe and are not billed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/rate_limited",
                  "title": "Too many requests for this key",
                  "status": 429,
                  "code": "rate_limited"
                }
              }
            }
          },
          "500": {
            "description": "internal — Something failed on our side. Nothing was charged. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/internal",
                  "title": "Something failed on our side",
                  "status": 500,
                  "code": "internal"
                }
              }
            }
          }
        }
      }
    },
    "/company/enrich": {
      "post": {
        "operationId": "companyEnrich",
        "summary": "Enrich a company",
        "description": "Return the full company record — firmographics, location, size and history.\n\nOne credit on a successful match. A miss answers 200 with matched:false and charges nothing — it is an outcome, not an error. Send an Idempotency-Key header and a retried request replays the original response instead of re-executing, so a timeout cannot double-charge you.",
        "externalDocs": {
          "description": "Reference page",
          "url": "https://doc.truepoint.in/docs/api/company-enrich"
        },
        "x-availability": "beta",
        "x-credits": 1,
        "security": [
          {
            "bearerAuth": [
              "search:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "A unique key per unit of work. Retrying with the same key replays the stored response and charges nothing.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain"
                ],
                "properties": {
                  "domain": {
                    "type": "string",
                    "description": "A registrable domain, normalised as for /match."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The lookup ran. Check `matched` — a miss is a normal 200, never a 404.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "matched": {
                      "type": "boolean",
                      "description": "Whether a company was found."
                    },
                    "credits_charged": {
                      "type": "number",
                      "description": "1 on a match, 0 on a miss."
                    },
                    "credits_remaining": {
                      "type": "number",
                      "description": "Your balance after this call."
                    },
                    "company": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "Null when nothing matched — see `matched`.",
                      "properties": {
                        "domain": {
                          "type": "string",
                          "description": "Canonical registrable domain."
                        },
                        "name": {
                          "type": "string",
                          "description": "Canonical company name."
                        },
                        "website_url": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Primary website."
                        },
                        "description": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Company description."
                        },
                        "industry": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Canonical industry label where we resolved one, otherwise the raw spelling."
                        },
                        "employee_count": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "Current headcount estimate."
                        },
                        "revenue_range": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "A band rather than a figure. We publish what the data supports; an exact number would imply a precision we do not have."
                        },
                        "ownership_type": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Public, private, and so on."
                        },
                        "year_founded": {
                          "type": [
                            "number",
                            "null"
                          ],
                          "description": "Year founded."
                        },
                        "specialties": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Self-described specialties."
                        },
                        "hq_country": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Headquarters country."
                        },
                        "hq_city": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Headquarters city."
                        },
                        "last_updated": {
                          "type": "string",
                          "description": "ISO-8601. When the graph last changed this record — the freshness signal to decide staleness against."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "matched": true,
                  "company": {
                    "domain": "northgate.example.com",
                    "name": "Northgate Tax Partners",
                    "website_url": "https://northgate.example.com",
                    "description": "Regional tax advisory and audit practice.",
                    "industry": "Accounting",
                    "employee_count": 312,
                    "revenue_range": "$10M–$50M",
                    "ownership_type": "private",
                    "year_founded": 1948,
                    "specialties": [
                      "tax advisory",
                      "audit"
                    ],
                    "hq_country": "IN",
                    "hq_city": "Pune",
                    "last_updated": "2026-08-11T09:14:22.000Z"
                  },
                  "credits_charged": 1,
                  "credits_remaining": 4821
                }
              }
            }
          },
          "401": {
            "description": "invalid_token — Missing, malformed, unknown or revoked API key. All four answer identically on purpose — the endpoint must not confirm which keys exist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/invalid_token",
                  "title": "Missing, malformed, unknown or revoked API key",
                  "status": 401,
                  "code": "invalid_token"
                }
              }
            }
          },
          "402": {
            "description": "insufficient_credits — The balance cannot cover this call. Nothing was charged and nothing was returned. Carries `balance` and `required`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/insufficient_credits",
                  "title": "The balance cannot cover this call",
                  "status": 402,
                  "code": "insufficient_credits"
                }
              }
            }
          },
          "403": {
            "description": "insufficient_scope — The key is valid but was not minted with the scope this endpoint requires.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/insufficient_scope",
                  "title": "The key is valid but was not minted with the scope this endpoint requires",
                  "status": 403,
                  "code": "insufficient_scope"
                }
              }
            }
          },
          "422": {
            "description": "validation_error — The request failed validation — a missing identifier, or a value we could not parse. 422 rather than 400: the request was well-formed, its contents were not.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/validation_error",
                  "title": "The request failed validation — a missing identifier, or a value we could not parse",
                  "status": 422,
                  "code": "validation_error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited — Too many requests for this key. Carries `retryAfterSeconds`. Retries are safe and are not billed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/rate_limited",
                  "title": "Too many requests for this key",
                  "status": 429,
                  "code": "rate_limited"
                }
              }
            }
          },
          "500": {
            "description": "internal — Something failed on our side. Nothing was charged. Retry with backoff.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://truepoint.in/errors/internal",
                  "title": "Something failed on our side",
                  "status": 500,
                  "code": "internal"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A workspace API key, sent as `Authorization: Bearer <key>`. It is a server-side credential — never ship one to a browser or any client a user controls."
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "An RFC 9457 problem document. Branch on `code`, never on `title` — titles are prose and may be reworded.",
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "code": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          },
          "requestId": {
            "type": "string"
          },
          "retryAfterSeconds": {
            "type": "integer"
          },
          "balance": {
            "type": "integer"
          },
          "required": {
            "type": "integer"
          }
        }
      }
    }
  }
}
