{"openapi":"3.1.0","info":{"title":"Food Integrity Project API","version":"2.0.0","summary":"Verified products, and (for technical administrators) verification management.","description":"The Food Integrity Project's API for Non-GMO Project Verified and Non-UPF Verified products.\n\n- **Catalog:** listed products, brands, categories, the verify lookup, a change feed and a full export. Anyone may call it without a key, at a low rate limit; a key raises the limit and adds the token-only fields.\n- **Verification management and prospects:** a technical administrator's clients, brands, products, packages, verifications, facilities, certificates and certificate PDFs, and the opportunities shared with it. A TA's key sees only its own TA's records.\n- **Signing in:** send a token as `Authorization: Bearer <token>`. On the developer site your token is ready when you're signed in; integrations get one with `POST /v2/oauth/token`.\n- **Sandbox:** every endpoint is also under `/sandbox/v2`. Sandbox keys work only there.\n\nGuides, keys and the request log: https://fip-os-api.vercel.app/docs","contact":{"name":"Food Integrity Project developer site","url":"https://fip-os-api.vercel.app/docs"}},"servers":[{"url":"https://fip-os-api.vercel.app/v2","description":"Production"},{"url":"https://fip-os-api.vercel.app/sandbox/v2","description":"Sandbox"}],"tags":[{"name":"Catalog","description":"Listed products, brands and categories. Only what the listing rule lists is ever returned."},{"name":"Verification management","description":"A technical administrator's clients, brands, products, packages, verifications, facilities and certificates. A TA's key sees only its own TA's records, and past cycles of products now with it; staff see every TA's."},{"name":"Prospects","description":"Opportunities shared with your TA (or with every TA), and your TA's own. Only the parts the share allows; contacts only when the share includes them and the person agreed."},{"name":"Webhooks","description":"Get told when something changes instead of asking. Each delivery is a POST of the event as JSON:\n\n```json\n{ \"id\": \"…\", \"type\": \"verification.status_changed\", \"created_at\": \"…\", \"environment\": \"production\", \"delivery\": \"…\", \"attempt\": 1, \"data\": { … } }\n```\n\n**Check the signature.** `FIP-Signature: t=<unix seconds>,v1=<hex>` where the hex is HMAC-SHA256 of `<t>.<raw body>` with your webhook's secret. Compare in constant time and refuse a `t` more than five minutes old. For 24 hours after you rotate the secret, a second `v1` signed with the old one is added.\n\n**Answer quickly** with any 2xx status (within 10 seconds). Anything else is retried after 1, 5, 15 and 30 minutes, then every hour or so up to six hours apart, for 24 hours. Redirects aren't followed. Use the event's `id` to ignore one you've already handled: replays and retries send the same id."},{"name":"Writes","description":"Changes for technical administrators: products, packages, verifications and their actions, facilities on a verification, clients and brands.\n\n- **Who:** in production, a TA's key (`verification.write`; `client.write` for clients and brands), for its own TA's records only. Your own token can try any of them in the sandbox.\n- **Where it goes:** while Salesforce is the system of record, each change is saved in Salesforce first, as the FIP-OS System Writer on your TA's behalf, and answered from Salesforce's reply. The read endpoints show it within about five minutes.\n- **Sandbox:** every check runs, and the answer shows exactly what would be written (`would_write`); nothing is saved.\n- **Safe retries:** send an `Idempotency-Key`. Send `If-Match` with the ETag from your last GET to refuse a change on top of someone else's.\n- **Statuses** change only by action (hold, release, withdraw, terminate, transfer, cancel, renew), never by PATCH.\n- **Switched on by kind:** each kind of change is switched on separately; until then it answers 503 `writes_not_switched_on`."},{"name":"Signing in","description":"Tokens for integrations, and who a token belongs to."}],"security":[{"bearerAuth":[]}],"paths":{"/oauth/token":{"post":{"tags":["Signing in"],"operationId":"getToken","summary":"Get a token for a key","description":"Client credentials (OAuth 2.1): a key proves itself with its secret (HTTP Basic, or `client_id` and `client_secret` in the form) or with `private_key_jwt` (a JWT signed with the key's private key, `iss` and `sub` = the client id, `aud` = this endpoint's address, up to 10 minutes old). The token lasts an hour. Ask for fewer scopes with `scope`; never more than the key has.","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["client_credentials"]},"client_id":{"type":"string","format":"uuid"},"client_secret":{"type":"string"},"client_assertion_type":{"type":"string","enum":["urn:ietf:params:oauth:client-assertion-type:jwt-bearer"]},"client_assertion":{"type":"string","description":"The signed JWT, for private_key_jwt"},"scope":{"type":"string","description":"Space-separated; a subset of the key's scopes"}}},"example":{"grant_type":"client_credentials","client_id":"01a1b2c3-0000-7000-8000-000000000001","client_secret":"fipos_cs_…"}}}},"responses":{"200":{"description":"A token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Token"},"example":{"access_token":"fipos_at_…","token_type":"Bearer","expires_in":3600,"scope":"catalog.read verification.read"}}}},"400":{"description":"Wrong grant type or request (`error`, as OAuth defines)"},"401":{"description":"The key, its secret or its assertion isn't valid (`invalid_client`)"}}}},"/me":{"get":{"tags":["Signing in"],"operationId":"getMe","summary":"Who this token is","description":"The caller behind the token: a key, a person's explorer or personal token, or nobody (public). Handy for checking a token works.","security":[{},{"bearerAuth":[]},{"oauth2":["catalog.read"]}],"responses":{"200":{"description":"The caller","content":{"application/json":{"example":{"kind":"client","name":"NSF integration","scopes":["catalog.read","verification.read"],"environment":"production"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/catalog/products":{"get":{"tags":["Catalog"],"operationId":"listProducts","summary":"List and search listed products","description":"Listed products, oldest first, a page at a time: pass the response's `next_cursor` as `cursor` for the next page. Search by name or brand (`q`), barcode (`gtin`), certificate number, brand number, program or category; `updated_since` gives only what changed. Package details and other certifications need `catalog.read.extended`.","security":[{},{"bearerAuth":[]},{"oauth2":["catalog.read"]}],"parameters":[{"name":"q","in":"query","description":"Words in the product or brand name","schema":{"type":"string"},"example":"sparkling water"},{"name":"gtin","in":"query","description":"A barcode, 8 to 14 digits","schema":{"type":"string","pattern":"^\\d{8,14}$"}},{"name":"certificate","in":"query","description":"A certificate number","schema":{"type":"string"},"example":"C-670849-2026"},{"name":"brand_number","in":"query","description":"A brand's number","schema":{"type":"string"}},{"name":"program","in":"query","schema":{"type":"string","enum":["non_gmo","non_upf"]}},{"name":"category","in":"query","description":"A category code, from /catalog/categories","schema":{"type":"string"}},{"name":"updated_since","in":"query","description":"Only products changed since this ISO 8601 time","schema":{"type":"string","format":"date-time"}},{"name":"cursor","in":"query","description":"`next_cursor` from the previous page","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results a page, 1 to 200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"A page of products","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductPage"},"example":{"data":[{"id":"01a18995-a4f3-755a-9e8f-383bc25aa232","name":"Sparkling Water Craft Brewed with Citra Hops","ta_product_id":"2-82426-05001","single_ingredient":false,"brand":{"number":"17745","name":"Hoplark"},"participant":"Brooklyn Brewery","verifications":[{"program":"non_gmo","status":"verified","certificate_number":"C-670849-2026","valid_from":"2026-04-14","valid_to":"2027-04-13","scope":"Goods for human or pet ingestion or topical application","risk_status":"standard","technical_administrator":"NSF","listed":true}],"categories":[{"code":"beverages","name":"Beverages","group":"Beverages","primary":true}],"gtins":["00850019428060"],"updated_at":"2026-11-10T18:22:04Z"}],"next_cursor":"01a18995-a4f3-755a-9e8f-383bc25aa232"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/catalog/products/{id}":{"get":{"tags":["Catalog"],"operationId":"getProduct","summary":"Get a listed product","description":"One listed product by its FIP-OS id or its TA product ID. A product that isn't listed returns 404.","security":[{},{"bearerAuth":[]},{"oauth2":["catalog.read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"FIP-OS id (UUID) or TA product ID","schema":{"type":"string"},"example":"2-82426-05001"}],"responses":{"200":{"description":"The product","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"},"example":{"id":"01a18995-a4f3-755a-9e8f-383bc25aa232","name":"Sparkling Water Craft Brewed with Citra Hops","ta_product_id":"2-82426-05001","single_ingredient":false,"brand":{"number":"17745","name":"Hoplark"},"participant":"Brooklyn Brewery","verifications":[{"program":"non_gmo","status":"verified","certificate_number":"C-670849-2026","valid_from":"2026-04-14","valid_to":"2027-04-13","scope":"Goods for human or pet ingestion or topical application","risk_status":"standard","technical_administrator":"NSF","listed":true}],"categories":[{"code":"beverages","name":"Beverages","group":"Beverages","primary":true}],"gtins":["00850019428060"],"updated_at":"2026-11-10T18:22:04Z"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not listed, or no such product","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/catalog/brands":{"get":{"tags":["Catalog"],"operationId":"listBrands","summary":"List brands with listed products","description":"Brands that have at least one listed product, by brand number, a page at a time.","security":[{},{"bearerAuth":[]},{"oauth2":["catalog.read"]}],"parameters":[{"name":"q","in":"query","description":"Words in the brand name","schema":{"type":"string"}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results a page, 1 to 200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"A page of brands","content":{"application/json":{"example":{"data":[{"number":"17745","name":"Hoplark","listed_products":12}],"next_cursor":"17745"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/catalog/brands/{number}":{"get":{"tags":["Catalog"],"operationId":"getBrand","summary":"Get a brand","security":[{},{"bearerAuth":[]},{"oauth2":["catalog.read"]}],"parameters":[{"name":"number","in":"path","required":true,"schema":{"type":"string"},"example":"17745"}],"responses":{"200":{"description":"The brand","content":{"application/json":{"example":{"number":"17745","name":"Hoplark","website":"https://www.hoplark.com","listed_products":12,"programs":["non_gmo"]}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such brand with listed products","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/catalog/categories":{"get":{"tags":["Catalog"],"operationId":"listCategories","summary":"List categories by certificate group","security":[{},{"bearerAuth":[]},{"oauth2":["catalog.read"]}],"responses":{"200":{"description":"Categories","content":{"application/json":{"example":{"data":[{"group":"Beverages","categories":[{"code":"beverages","name":"Beverages"}]}]}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/catalog/lookup":{"get":{"tags":["Catalog"],"operationId":"lookup","summary":"Verify a product","description":"The verify answer for a barcode, certificate number or TA product ID, with `listed` explicit: what a QR code or a scanner app asks.","security":[{},{"bearerAuth":[]},{"oauth2":["catalog.read"]}],"parameters":[{"name":"gtin","in":"query","schema":{"type":"string"}},{"name":"certificate","in":"query","schema":{"type":"string"},"example":"C-670849-2026"},{"name":"product","in":"query","description":"TA product ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Found and listed, or not","content":{"application/json":{"example":{"found":true,"listed":true,"product":{"id":"01a18995-a4f3-755a-9e8f-383bc25aa232","name":"Sparkling Water Craft Brewed with Citra Hops","ta_product_id":"2-82426-05001","single_ingredient":false,"brand":{"number":"17745","name":"Hoplark"},"participant":"Brooklyn Brewery","verifications":[{"program":"non_gmo","status":"verified","certificate_number":"C-670849-2026","valid_from":"2026-04-14","valid_to":"2027-04-13","scope":"Goods for human or pet ingestion or topical application","risk_status":"standard","technical_administrator":"NSF","listed":true}],"categories":[{"code":"beverages","name":"Beverages","group":"Beverages","primary":true}],"gtins":["00850019428060"],"updated_at":"2026-11-10T18:22:04Z"}}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/catalog/changes":{"get":{"tags":["Catalog"],"operationId":"listChanges","summary":"What changed in the listing","description":"Every listing change (newly listed, changed, delisted) since a cursor, oldest first, recorded every 15 minutes. Start with `since=0`, keep `next_cursor`, and ask again with it to keep a copy up to date without re-pulling.","security":[{},{"bearerAuth":[]},{"oauth2":["catalog.read"]}],"parameters":[{"name":"since","in":"query","description":"The `next_cursor` you kept, or 0","schema":{"type":"integer","minimum":0},"example":0},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}}],"responses":{"200":{"description":"Changes","content":{"application/json":{"example":{"data":[{"cursor":1042,"at":"2026-11-11T19:30:00Z","change":"listed","program":"non_gmo","product_id":"01a18995-a4f3-755a-9e8f-383bc25aa232","product":{"id":"01a18995-a4f3-755a-9e8f-383bc25aa232","name":"Sparkling Water Craft Brewed with Citra Hops","ta_product_id":"2-82426-05001","single_ingredient":false,"brand":{"number":"17745","name":"Hoplark"},"participant":"Brooklyn Brewery","verifications":[{"program":"non_gmo","status":"verified","certificate_number":"C-670849-2026","valid_from":"2026-04-14","valid_to":"2027-04-13","scope":"Goods for human or pet ingestion or topical application","risk_status":"standard","technical_administrator":"NSF","listed":true}],"categories":[{"code":"beverages","name":"Beverages","group":"Beverages","primary":true}],"gtins":["00850019428060"],"updated_at":"2026-11-10T18:22:04Z"}}],"next_cursor":1042}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/catalog/export":{"get":{"tags":["Catalog"],"operationId":"exportCatalog","summary":"Download everything listed","description":"Every listed product as NDJSON (one product a line, the same as the product object) or CSV (one line per program), streamed. Large: tens of thousands of products.","security":[{},{"bearerAuth":[]},{"oauth2":["catalog.read"]}],"parameters":[{"name":"format","in":"query","schema":{"type":"string","enum":["ndjson","csv"],"default":"ndjson"}}],"responses":{"200":{"description":"The listing","content":{"application/x-ndjson":{"schema":{"type":"string"}},"text/csv":{"schema":{"type":"string"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/clients":{"get":{"tags":["Verification management"],"operationId":"listClients","summary":"List your clients","description":"Your TA's clients (enrollments): the company, its address and contacts, the program and status, and the dates. Was v1's `GET /application`: use `status=approved&updated_since=` for newly approved clients.","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"status","in":"query","description":"A status","schema":{"type":"string","enum":["submitted","in_review","approved","withdrawn","transferred","terminated"]}},{"name":"program","in":"query","description":"A program","schema":{"type":"string","enum":["non_gmo","non_upf"]}},{"name":"q","in":"query","description":"Company name or client number","schema":{"type":"string"}},{"name":"updated_since","in":"query","description":"Only what changed since this ISO 8601 time","schema":{"type":"string","format":"date-time"}},{"name":"ta","in":"query","description":"Staff only: one TA's records, by code (`fcid`, `nsf`, `scs`, `wfcf`). A TA's key always sees only its own.","schema":{"type":"string"}},{"name":"cursor","in":"query","description":"`next_cursor` from the previous page","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results a page, 1 to 200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"A page of clients","content":{"application/json":{"example":{"data":[{"id":"01a1c2d3-0000-7000-8000-0000000000ff","client_number":"12345","program":"non_gmo","status":"approved","technical_administrator":"nsf","organization":{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc.","dba_name":null,"website":"https://example.com"},"address":{"line1":"1 Example Way","line2":null,"city":"Bellingham","region":"WA","postcode":"98225","country":"US"},"contacts":[{"role":"main_contact","name":"Sam Example","title":"Quality manager","email":"sam@example.com"}],"applied_on":"2024-04-01","approved_on":"2024-04-20","enrolled_on":"2024-04-20","withdrawn_on":null,"transferring":false,"products":42,"created_at":"2024-04-01T16:00:00Z","updated_at":"2026-10-01T16:00:00Z"}],"next_cursor":"01a1c2d3-0000-7000-8000-000000000123"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Writes"],"operationId":"createClient","summary":"Send a client application","description":"A new client for your TA (was v1's POST /client). The answer has its client number. Staff review it; `client.application_reviewed` tells you when it's decided.","security":[{"bearerAuth":[]},{"oauth2":["client.write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["company_name","address","programs"],"properties":{"company_name":{"type":"string"},"address":{"type":"object","required":["line1","postcode","country"],"properties":{"line1":{"type":"string"},"line2":{"type":"string"},"city":{"type":"string"},"region":{"type":"string"},"postcode":{"type":"string"},"country":{"type":"string"}}},"programs":{"type":"array","items":{"type":"string","enum":["non_gmo","non_upf"]}},"transferring":{"type":"boolean","default":false},"contacts":{"type":"array","items":{"type":"object","required":["role"],"properties":{"role":{"type":"string","enum":["main","secondary","marketing"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"title":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string","format":"email"}}}}}},"example":{"company_name":"Example Foods Inc.","address":{"line1":"1 Example Way","city":"Bellingham","region":"WA","postcode":"98225","country":"US"},"programs":["non_gmo"],"contacts":[{"role":"main","first_name":"Sam","last_name":"Example","email":"sam@example.com"}]}}}},"responses":{"201":{"description":"Sent","content":{"application/json":{"example":{"client_number":"C-012345","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/clients/{id}":{"get":{"tags":["Verification management"],"operationId":"getClient","summary":"Get a client","description":"One client by its id or client number.","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Client id (UUID) or client number","schema":{"type":"string"},"example":"12345"}],"responses":{"200":{"description":"The client","content":{"application/json":{"example":{"id":"01a1c2d3-0000-7000-8000-0000000000ff","client_number":"12345","program":"non_gmo","status":"approved","technical_administrator":"nsf","organization":{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc.","dba_name":null,"website":"https://example.com"},"address":{"line1":"1 Example Way","line2":null,"city":"Bellingham","region":"WA","postcode":"98225","country":"US"},"contacts":[{"role":"main_contact","name":"Sam Example","title":"Quality manager","email":"sam@example.com"}],"applied_on":"2024-04-01","approved_on":"2024-04-20","enrolled_on":"2024-04-20","withdrawn_on":null,"transferring":false,"products":42,"created_at":"2024-04-01T16:00:00Z","updated_at":"2026-10-01T16:00:00Z"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That client wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Writes"],"operationId":"patchClient","summary":"Change a client","description":"Address, contacts, company name, programs. The status changes by action.","security":[{"bearerAuth":[]},{"oauth2":["client.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Client id or client number","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"},"example":{"contacts":[{"role":"secondary","first_name":"Alex","last_name":"Example","email":"alex@example.com"}]}}}},"responses":{"200":{"description":"Changed","content":{"application/json":{"example":{"client":"12345","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/brands":{"get":{"tags":["Verification management"],"operationId":"listTaBrands","summary":"List brands on your products","description":"Brands with at least one of your TA's products, by brand number, with their owners (the company that legally owns the brand, which may differ from the participant).","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"q","in":"query","description":"Words in the brand name","schema":{"type":"string"}},{"name":"ta","in":"query","description":"Staff only: one TA's records, by code (`fcid`, `nsf`, `scs`, `wfcf`). A TA's key always sees only its own.","schema":{"type":"string"}},{"name":"cursor","in":"query","description":"`next_cursor` from the previous page","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results a page, 1 to 200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"A page of brands","content":{"application/json":{"example":{"data":[{"number":"123456","id":"01a1c2d3-0000-7000-8000-000000000111","name":"Example Brand","website":"https://example.com","owners":[{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc.","valid_from":"2020-01-01","valid_to":null}],"products":42,"updated_at":"2026-09-01T16:00:00Z"}],"next_cursor":"123456"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Writes"],"operationId":"createBrand","summary":"Add a brand to a client","description":"The brand, owned by the client's company, linked to the client. A brand with that name and owner already is reused (200).","security":[{"bearerAuth":[]},{"oauth2":["client.write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["client","name"],"properties":{"client":{"type":"string","description":"Client id or number"},"name":{"type":"string"}}},"example":{"client":"12345","name":"Example Brand"}}}},"responses":{"201":{"description":"Added","content":{"application/json":{"example":{"brand_number":"123457","name":"Example Brand","existing":false,"result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/brands/{number}":{"get":{"tags":["Verification management"],"operationId":"getTaBrand","summary":"Get a brand","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"number","in":"path","required":true,"description":"The brand number","schema":{"type":"string"},"example":"123456"}],"responses":{"200":{"description":"The brand","content":{"application/json":{"example":{"number":"123456","id":"01a1c2d3-0000-7000-8000-000000000111","name":"Example Brand","website":"https://example.com","owners":[{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc.","valid_from":"2020-01-01","valid_to":null}],"products":42,"updated_at":"2026-09-01T16:00:00Z"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That brand wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/products":{"get":{"tags":["Verification management"],"operationId":"listTaProducts","summary":"List your products","description":"Your TA's products with categories, other certifications, a count of packages and each program's current verification.","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"q","in":"query","description":"Words in the name, or a TA product ID","schema":{"type":"string"}},{"name":"brand_number","in":"query","description":"A brand number","schema":{"type":"string"}},{"name":"participant","in":"query","description":"A participant's id","schema":{"type":"string","format":"uuid"}},{"name":"program","in":"query","description":"A program","schema":{"type":"string","enum":["non_gmo","non_upf"]}},{"name":"updated_since","in":"query","description":"Only what changed since this ISO 8601 time","schema":{"type":"string","format":"date-time"}},{"name":"ta","in":"query","description":"Staff only: one TA's records, by code (`fcid`, `nsf`, `scs`, `wfcf`). A TA's key always sees only its own.","schema":{"type":"string"}},{"name":"cursor","in":"query","description":"`next_cursor` from the previous page","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results a page, 1 to 200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"A page of products","content":{"application/json":{"example":{"data":[{"id":"01a1c2d3-0000-7000-8000-0000000000aa","ta_product_id":"2-12345-00001","name":"Example Oat Crackers","technical_administrator":"nsf","participant":{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc."},"brand":{"number":"123456","name":"Example Brand"},"single_ingredient":false,"do_not_publish":false,"categories":[{"code":"crackers","name":"Crackers","group":"Grocery and beverages","primary":true}],"other_certifications":[{"type":"organic","name":"Organic","certifier":"Example Certifier","certificate":"ORG-1","valid_from":"2026-01-01","valid_to":"2027-01-01"}],"packages":3,"verifications":[{"ref":"V-0123456","program":"non_gmo","status":"verified","certificate_number":"C-654321-2026","valid_from":"2026-03-01","valid_to":"2027-02-28","listed":true}],"created_at":"2024-05-02T17:00:00Z","updated_at":"2026-10-30T16:12:00Z"}],"next_cursor":"01a1c2d3-0000-7000-8000-000000000123"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/products/{id}":{"get":{"tags":["Verification management"],"operationId":"getTaProduct","summary":"Get a product","description":"One product by its TA product ID or FIP-OS id. Not one of yours, or no such product: 404 (v1 answered 400 here; use this as the existence check).","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"TA product ID or FIP-OS id","schema":{"type":"string"},"example":"2-12345-00001"}],"responses":{"200":{"description":"The product","content":{"application/json":{"example":{"id":"01a1c2d3-0000-7000-8000-0000000000aa","ta_product_id":"2-12345-00001","name":"Example Oat Crackers","technical_administrator":"nsf","participant":{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc."},"brand":{"number":"123456","name":"Example Brand"},"single_ingredient":false,"do_not_publish":false,"categories":[{"code":"crackers","name":"Crackers","group":"Grocery and beverages","primary":true}],"other_certifications":[{"type":"organic","name":"Organic","certifier":"Example Certifier","certificate":"ORG-1","valid_from":"2026-01-01","valid_to":"2027-01-01"}],"packages":3,"verifications":[{"ref":"V-0123456","program":"non_gmo","status":"verified","certificate_number":"C-654321-2026","valid_from":"2026-03-01","valid_to":"2027-02-28","listed":true}],"created_at":"2024-05-02T17:00:00Z","updated_at":"2026-10-30T16:12:00Z"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That product wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"put":{"tags":["Writes"],"operationId":"putProduct","summary":"Add or update a product","description":"Creates the product under this TA product ID, or updates it when it's already yours (was v1's POST and PATCH /product). A TA product ID used by another TA is refused.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"TA product ID or FIP-OS id","schema":{"type":"string"},"example":"2-12345-00001"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":80,"description":"Required to create"},"trade_name":{"type":"string","description":"Defaults to the name"},"single_ingredient":{"type":"boolean","default":false},"ingredients":{"type":"string"},"participant_id":{"type":"string","format":"uuid","description":"Required to create: the participant's id (from GET /v2/clients). Can't be changed later"},"categories":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Category codes to add (from /v2/catalog/categories); ones it has are skipped"},"remove_categories":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Category codes to remove; ones it doesn't have are skipped"}}},"example":{"name":"Example Oat Crackers","participant_id":"01a1c2d3-0000-7000-8000-0000000000bb","categories":["crackers"]}}}},"responses":{"201":{"description":"Created (200 when updated)","content":{"application/json":{"example":{"ta_product_id":"2-12345-00001","created":true,"result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Writes"],"operationId":"patchProduct","summary":"Change a product","description":"Name, trade name, single ingredient, ingredients, or categories to add.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"TA product ID or FIP-OS id","schema":{"type":"string"},"example":"2-12345-00001"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":80,"description":"Required to create"},"trade_name":{"type":"string","description":"Defaults to the name"},"single_ingredient":{"type":"boolean","default":false},"ingredients":{"type":"string"},"participant_id":{"type":"string","format":"uuid","description":"Required to create: the participant's id (from GET /v2/clients). Can't be changed later"},"categories":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Category codes to add (from /v2/catalog/categories); ones it has are skipped"},"remove_categories":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Category codes to remove; ones it doesn't have are skipped"}}},"example":{"trade_name":"Oat Crackers, Sea Salt"}}}},"responses":{"200":{"description":"Changed","content":{"application/json":{"example":{"ta_product_id":"2-12345-00001","created":false,"result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/products/{id}/packages":{"get":{"tags":["Verification management"],"operationId":"listPackages","summary":"List a product's packages","description":"Packages with barcode, type, size and channel. Removed packages are left out unless `include_removed=true`.","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"TA product ID or FIP-OS id","schema":{"type":"string"},"example":"2-12345-00001"},{"name":"include_removed","in":"query","description":"Include removed packages","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"The packages","content":{"application/json":{"example":{"data":[{"id":"01a1c2d3-0000-7000-8000-0000000000cc","gtin":"00012345678905","code":"012345678905","code_type":"UPC","gtin_status":"valid","size":8,"unit":"oz","type":"box","channel":"retail","status":"active","removed_reason":null,"brand":{"number":"123456","name":"Example Brand"},"created_at":"2024-05-02T17:00:00Z","updated_at":"2026-10-30T16:12:00Z"}]}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That product wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Writes"],"operationId":"createPackage","summary":"Add a package","description":"A package with its barcode, brand, type, size and channel. It starts active.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"TA product ID or FIP-OS id","schema":{"type":"string"},"example":"2-12345-00001"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"The barcode or item number (required to add)"},"code_type":{"type":"string","enum":["UPC-A","UPC-E","EAN","Item Number","PLU","Other"]},"type":{"type":"string","enum":["Package","Box","Aseptic Container","Bag","Bucket","Bottle","Bar","Can","Case","Carton","Drum","Pouch","Shipping Container","Tote","Tin","Jar","Bunch","Dry Pint","Packet","Clamshell","Tank","Meat Cuts","Cups","N/A","TBD"]},"size":{"type":"number","minimum":0},"unit":{"type":"string","enum":["Count","Fluid ounces","Gallons","Grams","Kilograms","Liters","Milligrams","Milliliters","Ounces","Pint","Pounds","Quart","Ton","Bushel","Tablet","Capsule","N/A","TBD"]},"channel":{"type":"string","enum":["Retail","Input & Ingredient","Bulk Retail"]},"brand_number":{"type":"string","description":"Required to add"}}},"example":{"code":"012345678905","code_type":"UPC-A","brand_number":"123456","type":"Box","size":8,"unit":"Ounces","channel":"Retail"}}}},"responses":{"201":{"description":"Added","content":{"application/json":{"example":{"package_number":"PKG-0098765","product":"2-12345-00001","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/verifications":{"get":{"tags":["Verification management"],"operationId":"listVerifications","summary":"List your verifications","description":"Verification cycles with their dates, certificate, scope, risk, listing, countries of origin and facilities. `current=true` gives only each product's latest cycle per program.","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"status","in":"query","description":"A status","schema":{"type":"string","enum":["verified","on_hold","withdrawn","terminated","transferred","canceled"]}},{"name":"program","in":"query","description":"A program","schema":{"type":"string","enum":["non_gmo","non_upf"]}},{"name":"product","in":"query","description":"A TA product ID or product id","schema":{"type":"string"}},{"name":"current","in":"query","description":"Only the latest cycle of each product and program","schema":{"type":"boolean"}},{"name":"updated_since","in":"query","description":"Only what changed since this ISO 8601 time","schema":{"type":"string","format":"date-time"}},{"name":"ta","in":"query","description":"Staff only: one TA's records, by code (`fcid`, `nsf`, `scs`, `wfcf`). A TA's key always sees only its own.","schema":{"type":"string"}},{"name":"cursor","in":"query","description":"`next_cursor` from the previous page","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results a page, 1 to 200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"A page of verifications","content":{"application/json":{"example":{"data":[{"id":"01a1c2d3-0000-7000-8000-0000000000ee","ref":"V-0123456","program":"non_gmo","status":"verified","status_effective_on":"2026-03-01","technical_administrator":"nsf","product":{"id":"01a1c2d3-0000-7000-8000-0000000000aa","ta_product_id":"2-12345-00001","name":"Example Oat Crackers"},"participant":{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc."},"brand":{"number":"123456","name":"Example Brand"},"certificate_number":"C-654321-2026","valid_from":"2026-03-01","valid_to":"2027-02-28","evaluation_started_on":"2026-01-15","completed_on":"2026-02-27","scope":"Goods for human or pet ingestion or topical application","risk_status":"standard","listed":true,"current":true,"renews":"V-0101010","do_not_publish":false,"countries_of_origin":["CA","US"],"facilities":[{"facility_id":"01a1c2d3-0000-7000-8000-0000000000dd","name":"Example Bakery, Plant 2","role":"manufacturer","status":"active","inspected":true,"inspected_on":"2026-02-10"}],"created_at":"2026-01-15T18:00:00Z","updated_at":"2026-03-01T19:00:00Z"}],"next_cursor":"01a1c2d3-0000-7000-8000-000000000123"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/verifications/{ref}":{"get":{"tags":["Verification management"],"operationId":"getVerification","summary":"Get a verification","description":"One verification by its `V-` reference or id, with its status history.","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference or id","schema":{"type":"string"},"example":"V-0123456"}],"responses":{"200":{"description":"The verification","content":{"application/json":{"example":{"id":"01a1c2d3-0000-7000-8000-0000000000ee","ref":"V-0123456","program":"non_gmo","status":"verified","status_effective_on":"2026-03-01","technical_administrator":"nsf","product":{"id":"01a1c2d3-0000-7000-8000-0000000000aa","ta_product_id":"2-12345-00001","name":"Example Oat Crackers"},"participant":{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc."},"brand":{"number":"123456","name":"Example Brand"},"certificate_number":"C-654321-2026","valid_from":"2026-03-01","valid_to":"2027-02-28","evaluation_started_on":"2026-01-15","completed_on":"2026-02-27","scope":"Goods for human or pet ingestion or topical application","risk_status":"standard","listed":true,"current":true,"renews":"V-0101010","do_not_publish":false,"countries_of_origin":["CA","US"],"facilities":[{"facility_id":"01a1c2d3-0000-7000-8000-0000000000dd","name":"Example Bakery, Plant 2","role":"manufacturer","status":"active","inspected":true,"inspected_on":"2026-02-10"}],"created_at":"2026-01-15T18:00:00Z","updated_at":"2026-03-01T19:00:00Z","history":[{"from":null,"to":"verified","kind":"status_change","effective_on":"2026-03-01","recorded_at":"2026-03-01T19:00:00Z","reason":null,"approximate":false}]}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That verification wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Writes"],"operationId":"patchVerification","summary":"Change a verification's details","description":"Dates, scope, risk, countries and brand. Not the status: use an action.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference","schema":{"type":"string"},"example":"V-0123456"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"brand_number":{"type":"string"},"scope":{"type":"string"},"evaluation_started_on":{"type":"string","format":"date"},"completed_on":{"type":"string","format":"date","description":"More than 7 days back is accepted with a warning"},"valid_from":{"type":"string","format":"date"},"valid_to":{"type":"string","format":"date"},"countries_distributed":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$"},"description":"ISO 3166 codes"},"high_risk":{"type":"boolean"},"monitored_risk":{"type":"boolean"}}},"example":{"valid_to":"2027-11-30"}}}},"responses":{"200":{"description":"Changed","content":{"application/json":{"example":{"verification":"V-0123456","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/verifications/{ref}/facilities":{"get":{"tags":["Verification management"],"operationId":"listVerificationFacilities","summary":"List a verification's facilities","description":"Facility relationships: the facility, its role, and whether and when it was inspected.","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference or id","schema":{"type":"string"},"example":"V-0123456"}],"responses":{"200":{"description":"The facilities","content":{"application/json":{"example":{"data":[{"facility_id":"01a1c2d3-0000-7000-8000-0000000000dd","name":"Example Bakery, Plant 2","role":"manufacturer","status":"active","inspected":true,"inspected_on":"2026-02-10"}]}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That verification wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/verifications/{ref}/facilities/{facility_id}":{"get":{"tags":["Verification management"],"operationId":"getVerificationFacility","summary":"Get one facility on a verification","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference or id","schema":{"type":"string"},"example":"V-0123456"},{"name":"facility_id","in":"path","required":true,"description":"The facility's id","schema":{"type":"string"}}],"responses":{"200":{"description":"The facility relationship","content":{"application/json":{"example":{"facility_id":"01a1c2d3-0000-7000-8000-0000000000dd","name":"Example Bakery, Plant 2","role":"manufacturer","status":"active","inspected":true,"inspected_on":"2026-02-10"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That facility on this verification wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"put":{"tags":["Writes"],"operationId":"putVerificationFacility","summary":"Add or update a facility on a verification","description":"Adds the facility (`owned` is required then), or updates whether and when it was inspected. Facilities come from GET /v2/facilities; a new facility is added by staff for now.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference","schema":{"type":"string"},"example":"V-0123456"},{"name":"facility_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"owned":{"type":"boolean","description":"true: the participant's own; false: a co-packer"},"inspected":{"type":"boolean"},"inspected_on":{"type":"string","format":"date"}}},"example":{"owned":false,"inspected":true,"inspected_on":"2026-10-20"}}}},"responses":{"201":{"description":"Added (200 when updated)","content":{"application/json":{"example":{"verification":"V-0123456","facility_id":"01a1c2d3-0000-7000-8000-0000000000dd","added":true,"result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Writes"],"operationId":"removeVerificationFacility","summary":"Remove a facility from a verification","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference","schema":{"type":"string"},"example":"V-0123456"},{"name":"facility_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"responses":{"200":{"description":"Removed","content":{"application/json":{"example":{"verification":"V-0123456","facility_id":"01a1c2d3-0000-7000-8000-0000000000dd","status":"removed","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/facilities":{"get":{"tags":["Verification management"],"operationId":"listFacilities","summary":"List facilities on your verifications","description":"Facilities linked to your TA's verifications, with address, the companies they belong to and a count of your verifications there.","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"q","in":"query","description":"Words in the facility name","schema":{"type":"string"}},{"name":"ta","in":"query","description":"Staff only: one TA's records, by code (`fcid`, `nsf`, `scs`, `wfcf`). A TA's key always sees only its own.","schema":{"type":"string"}},{"name":"cursor","in":"query","description":"`next_cursor` from the previous page","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results a page, 1 to 200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"A page of facilities","content":{"application/json":{"example":{"data":[{"id":"01a1c2d3-0000-7000-8000-0000000000dd","name":"Example Bakery, Plant 2","kind":"manufacturing","address":{"line1":"200 Mill Road","line2":null,"city":"Guelph","region":"ON","postcode":"N1H 1A1","country":"CA"},"organizations":[{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc.","role":"owner"}],"verifications":17,"updated_at":"2026-06-01T16:00:00Z"}],"next_cursor":"01a1c2d3-0000-7000-8000-000000000123"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Writes"],"operationId":"createFacility","summary":"Register a facility","description":"Match first, then create: a facility with this name and postal code already is reused (200); otherwise it's added (201). Send your own `facility_id` to use it in place of FIP-OS's id, for example in PUT /v2/verifications/{ref}/facilities/{facility_id}.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","address"],"properties":{"name":{"type":"string"},"facility_id":{"type":"string","description":"Your own ID for it"},"address":{"type":"object","required":["line1","postcode","country"],"properties":{"line1":{"type":"string"},"line2":{"type":"string"},"city":{"type":"string"},"region":{"type":"string"},"postcode":{"type":"string"},"country":{"type":"string"}}}}},"example":{"name":"Example Bakery, Plant 2","facility_id":"NSF-FAC-1234","address":{"line1":"200 Mill Road","city":"Guelph","region":"ON","postcode":"N1H 1A1","country":"Canada"}}}}},"responses":{"201":{"description":"Added (200 when an existing one matched)","content":{"application/json":{"example":{"id":null,"name":"Example Bakery, Plant 2","matched":false,"facility_id":"NSF-FAC-1234","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/facilities/{id}":{"get":{"tags":["Verification management"],"operationId":"getFacility","summary":"Get a facility","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The facility's id","schema":{"type":"string"}}],"responses":{"200":{"description":"The facility","content":{"application/json":{"example":{"id":"01a1c2d3-0000-7000-8000-0000000000dd","name":"Example Bakery, Plant 2","kind":"manufacturing","address":{"line1":"200 Mill Road","line2":null,"city":"Guelph","region":"ON","postcode":"N1H 1A1","country":"CA"},"organizations":[{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc.","role":"owner"}],"verifications":17,"updated_at":"2026-06-01T16:00:00Z"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That facility wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/certificates":{"get":{"tags":["Verification management"],"operationId":"listCertificates","summary":"List your certificates","description":"Each current cycle with a certificate number: status, scope, dates, whether it's listed, and whether the participant has a license agreement in force (a per-product flag, where v1's `GET /certificate` failed the whole call). Was v1's `GET /certificate`.","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"program","in":"query","description":"A program","schema":{"type":"string","enum":["non_gmo","non_upf"]}},{"name":"listed","in":"query","description":"Only listed (true) or unlisted (false)","schema":{"type":"boolean"}},{"name":"updated_since","in":"query","description":"Only what changed since this ISO 8601 time","schema":{"type":"string","format":"date-time"}},{"name":"ta","in":"query","description":"Staff only: one TA's records, by code (`fcid`, `nsf`, `scs`, `wfcf`). A TA's key always sees only its own.","schema":{"type":"string"}},{"name":"cursor","in":"query","description":"`next_cursor` from the previous page","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results a page, 1 to 200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"A page of certificates","content":{"application/json":{"example":{"data":[{"certificate_number":"C-654321-2026","program":"non_gmo","verification":"V-0123456","status":"verified","valid_from":"2026-03-01","valid_to":"2027-02-28","scope":"Goods for human or pet ingestion or topical application","listed":true,"license_in_force":true,"technical_administrator":"nsf","product":{"id":"01a1c2d3-0000-7000-8000-0000000000aa","ta_product_id":"2-12345-00001","name":"Example Oat Crackers"},"participant":{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc."},"brand":{"number":"123456","name":"Example Brand"},"updated_at":"2026-03-01T19:00:00Z"}],"next_cursor":"01a1c2d3-0000-7000-8000-000000000123"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/certificates/{number}":{"get":{"tags":["Verification management"],"operationId":"getCertificate","summary":"Get a certificate","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"number","in":"path","required":true,"description":"The certificate number","schema":{"type":"string"},"example":"C-654321-2026"}],"responses":{"200":{"description":"The certificate","content":{"application/json":{"example":{"certificate_number":"C-654321-2026","program":"non_gmo","verification":"V-0123456","status":"verified","valid_from":"2026-03-01","valid_to":"2027-02-28","scope":"Goods for human or pet ingestion or topical application","listed":true,"license_in_force":true,"technical_administrator":"nsf","product":{"id":"01a1c2d3-0000-7000-8000-0000000000aa","ta_product_id":"2-12345-00001","name":"Example Oat Crackers"},"participant":{"id":"01a1c2d3-0000-7000-8000-0000000000bb","name":"Example Foods Inc."},"brand":{"number":"123456","name":"Example Brand"},"updated_at":"2026-03-01T19:00:00Z"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That certificate wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/certificates/pdf":{"post":{"tags":["Verification management"],"operationId":"createCertificatePdf","summary":"Make a certificate PDF","description":"A Certificate of Verification for one product, one certificate, a participant, a brand, or a custom list of up to 1,000 products. Answers at once with the job and its `pdf_url`, a lasting link that always gives today's certificate for the request (it's made from the record each time it's opened). Summaries for a participant or brand list only your TA's lines. Replaces the certificate (COV) API's `generate`, `generate-custom` and `retrieve`.","security":[{"bearerAuth":[]},{"oauth2":["certificate.issue"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["kind"],"properties":{"kind":{"type":"string","enum":["product","certificate","participant","brand","custom"]},"program":{"type":"string","enum":["non_gmo","non_upf"],"default":"non_gmo"},"product":{"type":"string","description":"For kind product: a TA product ID"},"certificate_number":{"type":"string","description":"For kind certificate"},"participant_id":{"type":"string","format":"uuid","description":"For kind participant, and optionally brand"},"brand_id":{"type":"string","format":"uuid","description":"For kind brand"},"products":{"type":"array","items":{"type":"string"},"maxItems":1000,"description":"For kind custom: TA product IDs"},"title":{"type":"string","maxLength":120,"description":"A title for a custom list"},"paper":{"type":"string","enum":["letter","a4"],"default":"letter"}}},"example":{"kind":"product","program":"non_gmo","product":"2-12345-00001"}}}},"responses":{"201":{"description":"The job, ready","content":{"application/json":{"example":{"job":"Q7Z2K9M4PX","status":"ready","kind":"product","program":"non_gmo","request":{"product_ref":"2-12345-00001"},"title":null,"paper":"letter","pdf_url":"https://app.foodintegrityproject.org/cov/Q7Z2K9M4PX","created_at":"2026-11-11T20:00:00Z","note":"The link always gives today's certificate for this request; products marked do not publish are left out."}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That product, certificate or list wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/certificates/pdf/{job}":{"get":{"tags":["Verification management"],"operationId":"getCertificatePdf","summary":"Get a certificate job","description":"A certificate job you (or someone at your TA) asked for, with its `pdf_url`.","security":[{"bearerAuth":[]},{"oauth2":["certificate.issue"]}],"parameters":[{"name":"job","in":"path","required":true,"description":"The job from POST /certificates/pdf","schema":{"type":"string"},"example":"Q7Z2K9M4PX"}],"responses":{"200":{"description":"The job","content":{"application/json":{"example":{"job":"Q7Z2K9M4PX","status":"ready","kind":"product","program":"non_gmo","request":{"product_ref":"2-12345-00001"},"title":null,"paper":"letter","pdf_url":"https://app.foodintegrityproject.org/cov/Q7Z2K9M4PX","created_at":"2026-11-11T20:00:00Z","note":"The link always gives today's certificate for this request; products marked do not publish are left out."}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That certificate job wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/opportunities":{"get":{"tags":["Prospects"],"operationId":"listOpportunities","summary":"List opportunities shared with you","description":"Opportunities shared with your TA or with every TA, and your TA's own, with the share's access and scope. Was v1's `GET /prospect`: use `updated_since` for new ones.","security":[{"bearerAuth":[]},{"oauth2":["prospect.read"]}],"parameters":[{"name":"stage","in":"query","description":"A stage","schema":{"type":"string","enum":["new","contacted","qualified","nurture","won","lost"]}},{"name":"updated_since","in":"query","description":"Only what changed since this ISO 8601 time","schema":{"type":"string","format":"date-time"}},{"name":"ta","in":"query","description":"Staff only: one TA's records, by code (`fcid`, `nsf`, `scs`, `wfcf`). A TA's key always sees only its own.","schema":{"type":"string"}},{"name":"cursor","in":"query","description":"`next_cursor` from the previous page","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Results a page, 1 to 200 (default 50).","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"A page of opportunities","content":{"application/json":{"example":{"data":[{"id":"01a1c2d3-0000-7000-8000-000000000222","kind":"new_client","stage":"qualified","owned":false,"company_name":"Example Snacks LLC","product_description":"Three flavors of kettle chips","product_count":3,"programs":["non_gmo"],"opened_on":"2026-10-02","closed_on":null,"share":{"access":"view","scope":["summary","contacts"],"status":null,"shared_at":"2026-10-03T16:00:00Z","note":null},"contacts":[{"name":"Alex Example","role":"submitter","title":"Founder","email":"alex@example.com"}],"updated_at":"2026-10-03T16:00:00Z"}],"next_cursor":"01a1c2d3-0000-7000-8000-000000000123"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/demand":{"get":{"tags":["Prospects"],"operationId":"getDemand","summary":"Demand for your products","description":"Community requests (\"please verify this\") and scan-app look-ups that name your TA's products, by barcode, TA product ID or brand, since `days` ago, most first. Scans count once per app per 2-second burst.","security":[{"bearerAuth":[]},{"oauth2":["prospect.read"]}],"parameters":[{"name":"days","in":"query","description":"How far back, 1 to 365 (default 90)","schema":{"type":"integer","minimum":1,"maximum":365,"default":90}},{"name":"ta","in":"query","description":"Staff only: one TA's records, by code (`fcid`, `nsf`, `scs`, `wfcf`). A TA's key always sees only its own.","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Products and brands, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Demand","content":{"application/json":{"example":{"since":"2026-08-14T00:00:00Z","products":[{"product":{"id":"01a1c2d3-0000-7000-8000-0000000000aa","ta_product_id":"2-12345-00001","name":"Example Oat Crackers"},"brand":{"number":"123456","name":"Example Brand"},"scans":14,"last_scanned_at":"2026-11-10T18:00:00Z","requests":3,"last_requested_at":"2026-11-02T16:00:00Z"}],"brands":[{"number":"123456","name":"Example Brand","requests":2,"last_requested_at":"2026-10-30T16:00:00Z"}]}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/opportunities/{id}":{"get":{"tags":["Prospects"],"operationId":"getOpportunity","summary":"Get an opportunity","security":[{"bearerAuth":[]},{"oauth2":["prospect.read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The opportunity's id","schema":{"type":"string"}}],"responses":{"200":{"description":"The opportunity","content":{"application/json":{"example":{"id":"01a1c2d3-0000-7000-8000-000000000222","kind":"new_client","stage":"qualified","owned":false,"company_name":"Example Snacks LLC","product_description":"Three flavors of kettle chips","product_count":3,"programs":["non_gmo"],"opened_on":"2026-10-02","closed_on":null,"share":{"access":"view","scope":["summary","contacts"],"status":null,"shared_at":"2026-10-03T16:00:00Z","note":null},"contacts":[{"name":"Alex Example","role":"submitter","title":"Founder","email":"alex@example.com"}],"updated_at":"2026-10-03T16:00:00Z"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`) or is for the other environment (`wrong_environment`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"That opportunity wasn't found, or isn't your TA's (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`); see RateLimit-Reset","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/event-types":{"get":{"tags":["Webhooks"],"operationId":"listEventTypes","summary":"List event types","description":"Every event a webhook can subscribe to, and the scope its owner needs to receive it.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The event types","content":{"application/json":{"example":{"data":[{"type":"verification.status_changed","scope":"verification.read","description":"A verification's status changed (verified, on hold, withdrawn, terminated, transferred, canceled)"},{"type":"verification.expiring","scope":"verification.read","description":"A current verification expires in 30 days"},{"type":"certificate.issued","scope":"verification.read","description":"A Certificate of Verification was issued for your TA's products"},{"type":"certificate.pdf_ready","scope":"certificate.issue","description":"A certificate PDF you asked for is ready"},{"type":"opportunity.shared","scope":"prospect.read","description":"An opportunity was shared with your TA or with every TA"},{"type":"opportunity.updated","scope":"prospect.read","description":"An opportunity shared with you, or your own, changed stage or details"},{"type":"client.application_reviewed","scope":"verification.read","description":"A client's application was reviewed (approved or otherwise decided)"},{"type":"catalog.listing_changed","scope":"catalog.read","description":"A product was newly listed, changed or delisted in the catalog"}]}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks":{"get":{"tags":["Webhooks"],"operationId":"listWebhooks","summary":"List your webhooks","description":"Your key's webhooks (or, with your own token, yours), with their deliveries in the last 24 hours.","security":[{"bearerAuth":[]},{"oauth2":["webhook.manage"]}],"responses":{"200":{"description":"Your webhooks","content":{"application/json":{"example":{"data":[{"id":"01a1c2d3-0000-7000-8000-000000000333","url":"https://example.com/fip-webhooks","events":["verification.status_changed","verification.expiring"],"description":"Status sync","environment":"production","created_at":"2026-11-11T20:00:00Z","failing_since":null,"deliveries":{"delivered_24h":42,"failed_24h":0,"waiting":0,"last_at":"2026-11-11T21:58:00Z"}}]}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Webhooks"],"operationId":"createWebhook","summary":"Add a webhook","description":"Subscribe an https address to event types. Its `secret` comes back once, here: keep it to check signatures. Up to 10 webhooks each. A webhook belongs to the environment of the key (or the URL, /v2 or /sandbox/v2) that made it; sandbox webhooks get test and replayed deliveries.","security":[{"bearerAuth":[]},{"oauth2":["webhook.manage"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri","description":"https, on the public internet"},"events":{"type":"array","items":{"type":"string","enum":["verification.status_changed","verification.expiring","certificate.issued","certificate.pdf_ready","opportunity.shared","opportunity.updated","client.application_reviewed","catalog.listing_changed"]}},"description":{"type":"string","maxLength":200}}},"example":{"url":"https://example.com/fip-webhooks","events":["verification.status_changed","verification.expiring"],"description":"Status sync"}}}},"responses":{"201":{"description":"The webhook, with its secret","content":{"application/json":{"example":{"id":"01a1c2d3-0000-7000-8000-000000000333","url":"https://example.com/fip-webhooks","events":["verification.status_changed","verification.expiring"],"description":"Status sync","environment":"production","created_at":"2026-11-11T20:00:00Z","failing_since":null,"deliveries":{"delivered_24h":42,"failed_24h":0,"waiting":0,"last_at":"2026-11-11T21:58:00Z"},"secret":"fipos_whsec_…","note":"Copy it now."}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{id}":{"get":{"tags":["Webhooks"],"operationId":"getWebhook","summary":"Get a webhook","security":[{"bearerAuth":[]},{"oauth2":["webhook.manage"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The webhook's id","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The webhook","content":{"application/json":{"example":{"id":"01a1c2d3-0000-7000-8000-000000000333","url":"https://example.com/fip-webhooks","events":["verification.status_changed","verification.expiring"],"description":"Status sync","environment":"production","created_at":"2026-11-11T20:00:00Z","failing_since":null,"deliveries":{"delivered_24h":42,"failed_24h":0,"waiting":0,"last_at":"2026-11-11T21:58:00Z"}}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such webhook or delivery of yours (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Webhooks"],"operationId":"deleteWebhook","summary":"Remove a webhook","description":"Stops its deliveries at once.","security":[{"bearerAuth":[]},{"oauth2":["webhook.manage"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The webhook's id","schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Removed"},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such webhook or delivery of yours (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{id}/rotate-secret":{"post":{"tags":["Webhooks"],"operationId":"rotateWebhookSecret","summary":"Rotate a webhook's secret","description":"A new secret, shown once. Deliveries carry signatures with both the new and the old secret for 24 hours.","security":[{"bearerAuth":[]},{"oauth2":["webhook.manage"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The webhook's id","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The new secret","content":{"application/json":{"example":{"secret":"fipos_whsec_…","note":"…"}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such webhook or delivery of yours (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{id}/test":{"post":{"tags":["Webhooks"],"operationId":"testWebhook","summary":"Send a test","description":"Sends a `webhook.test` event now and answers with how the delivery went.","security":[{"bearerAuth":[]},{"oauth2":["webhook.manage"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The webhook's id","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The delivery","content":{"application/json":{"example":{"id":"01a1c2d3-0000-7000-8000-000000000444","event":{"id":"01a1c2d3-0000-7000-8000-000000000555","type":"webhook.test","data":{"message":"A test from the Food Integrity Project API. Answer with any 2xx status."}},"status":"delivered","attempts":1,"last_status":200,"last_error":null,"created_at":"2026-11-11T21:58:00Z","last_attempt_at":"2026-11-11T21:58:01Z","delivered_at":"2026-11-11T21:58:01Z","next_attempt_at":null,"replay_of":null}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such webhook or delivery of yours (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{id}/deliveries":{"get":{"tags":["Webhooks"],"operationId":"listDeliveries","summary":"List a webhook's deliveries","description":"The delivery log, newest first, for 30 days: each event, its status, the attempts and the last answer.","security":[{"bearerAuth":[]},{"oauth2":["webhook.manage"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The webhook's id","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Deliveries","content":{"application/json":{"example":{"data":[{"id":"01a1c2d3-0000-7000-8000-000000000444","event":{"id":"01a1c2d3-0000-7000-8000-000000000555","type":"verification.status_changed","data":{"verification":"V-0123456","program":"non_gmo","from":"verified","to":"on_hold","effective_on":"2026-11-11","reason":"Requested by the participant","certificate_number":"C-654321-2026","product":{"id":"01a1c2d3-0000-7000-8000-0000000000aa","ta_product_id":"2-12345-00001","name":"Example Oat Crackers"}}},"status":"delivered","attempts":1,"last_status":200,"last_error":null,"created_at":"2026-11-11T21:58:00Z","last_attempt_at":"2026-11-11T21:58:01Z","delivered_at":"2026-11-11T21:58:01Z","next_attempt_at":null,"replay_of":null}]}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such webhook or delivery of yours (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/webhooks/{id}/deliveries/{delivery}/replay":{"post":{"tags":["Webhooks"],"operationId":"replayDelivery","summary":"Send a delivery again","description":"Sends the same event again now, as a new delivery (the event id is the same).","security":[{"bearerAuth":[]},{"oauth2":["webhook.manage"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The webhook's id","schema":{"type":"string","format":"uuid"}},{"name":"delivery","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"The new delivery","content":{"application/json":{"example":{"id":"01a1c2d3-0000-7000-8000-000000000444","event":{"id":"01a1c2d3-0000-7000-8000-000000000555","type":"verification.status_changed","data":{"verification":"V-0123456","program":"non_gmo","from":"verified","to":"on_hold","effective_on":"2026-11-11","reason":"Requested by the participant","certificate_number":"C-654321-2026","product":{"id":"01a1c2d3-0000-7000-8000-0000000000aa","ta_product_id":"2-12345-00001","name":"Example Oat Crackers"}}},"status":"delivered","attempts":1,"last_status":200,"last_error":null,"created_at":"2026-11-11T21:58:00Z","last_attempt_at":"2026-11-11T21:58:01Z","delivered_at":"2026-11-11T21:58:01Z","next_attempt_at":null,"replay_of":null}}}},"400":{"description":"The request isn't valid (code `invalid_request`, with the field)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (code `unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No such webhook or delivery of yours (code `not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/packages/{id}":{"patch":{"tags":["Writes"],"operationId":"patchPackage","summary":"Change a package","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The package's id (from GET /v2/products/{id}/packages)","schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"The barcode or item number (required to add)"},"code_type":{"type":"string","enum":["UPC-A","UPC-E","EAN","Item Number","PLU","Other"]},"type":{"type":"string","enum":["Package","Box","Aseptic Container","Bag","Bucket","Bottle","Bar","Can","Case","Carton","Drum","Pouch","Shipping Container","Tote","Tin","Jar","Bunch","Dry Pint","Packet","Clamshell","Tank","Meat Cuts","Cups","N/A","TBD"]},"size":{"type":"number","minimum":0},"unit":{"type":"string","enum":["Count","Fluid ounces","Gallons","Grams","Kilograms","Liters","Milligrams","Milliliters","Ounces","Pint","Pounds","Quart","Ton","Bushel","Tablet","Capsule","N/A","TBD"]},"channel":{"type":"string","enum":["Retail","Input & Ingredient","Bulk Retail"]},"brand_number":{"type":"string","description":"Required to add"}}},"example":{"size":10,"unit":"Ounces"}}}},"responses":{"200":{"description":"Changed","content":{"application/json":{"example":{"id":"01a1c2d3-0000-7000-8000-0000000000cc","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Writes"],"operationId":"removePackage","summary":"Remove a package","description":"Marks it removed, with the reason. It stays on record.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"reason","in":"query","required":true,"schema":{"type":"string","enum":["discontinued","duplicate"]}},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"responses":{"200":{"description":"Removed","content":{"application/json":{"example":{"id":"01a1c2d3-0000-7000-8000-0000000000cc","status":"removed","reason":"Discontinued","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/products/{id}/verifications":{"post":{"tags":["Writes"],"operationId":"createVerification","summary":"Add a verification","description":"A new cycle for the product, verified (or on hold). The answer has its `V-` reference. The participant defaults to the product's.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"TA product ID or FIP-OS id","schema":{"type":"string"},"example":"2-12345-00001"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["program","brand_number","valid_from","valid_to"],"properties":{"program":{"type":"string","enum":["non_gmo","non_upf"]},"brand_number":{"type":"string"},"participant_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["verified","on_hold"],"default":"verified"},"scope":{"type":"string"},"evaluation_started_on":{"type":"string","format":"date"},"completed_on":{"type":"string","format":"date","description":"More than 7 days back is accepted with a warning"},"valid_from":{"type":"string","format":"date"},"valid_to":{"type":"string","format":"date"},"countries_distributed":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$"},"description":"ISO 3166 codes"},"high_risk":{"type":"boolean"},"monitored_risk":{"type":"boolean"}}},"example":{"program":"non_gmo","brand_number":"123456","valid_from":"2026-11-01","valid_to":"2027-10-31","completed_on":"2026-11-01","scope":"Goods for human or pet ingestion or topical application"}}}},"responses":{"201":{"description":"Added","content":{"application/json":{"example":{"verification":"V-0123457","product":"2-12345-00001","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/verifications/{ref}/hold":{"post":{"tags":["Writes"],"operationId":"holdVerification","summary":"Put a verification on hold","description":"From verified. `on` defaults to today.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference","schema":{"type":"string"},"example":"V-0123456"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"on":{"type":"string","format":"date"}}},"example":{"on":"2026-11-11"}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"example":{"verification":"V-0123456","action":"hold","from":"Verified","to":"On Hold","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/verifications/{ref}/release":{"post":{"tags":["Writes"],"operationId":"releaseVerification","summary":"Release a verification from hold","description":"Back to verified.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference","schema":{"type":"string"},"example":"V-0123456"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"responses":{"200":{"description":"Done","content":{"application/json":{"example":{"verification":"V-0123456","action":"release","from":"Verified","to":"Verified","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/verifications/{ref}/withdraw":{"post":{"tags":["Writes"],"operationId":"withdrawVerification","summary":"Withdraw a verification","description":"Records when withdrawal was asked for (`requested_on`, default today) and, 180 days later, when the listing ends, as the Program Rules say.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference","schema":{"type":"string"},"example":"V-0123456"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"properties":{"requested_on":{"type":"string","format":"date"},"reason":{"type":"string","enum":["Cost Prohibitive","Unable to Achieve Verification","Lack of Value of Verification","Non-Responsive","Discontinued Product","Unresolved Major Non-Conformity","Willful Lying/Falsification","NGP Product License Termination","NGP Termination of License Agreement","Other","Plans to Re-enroll","Lost Buyer"]}}},"example":{"requested_on":"2026-11-11","reason":"Discontinued Product"}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"example":{"verification":"V-0123456","action":"withdraw","from":"Verified","to":"Withdrawn","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/verifications/{ref}/terminate":{"post":{"tags":["Writes"],"operationId":"terminateVerification","summary":"Terminate a verification","description":"`on` defaults to today.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference","schema":{"type":"string"},"example":"V-0123456"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"on":{"type":"string","format":"date"}}},"example":{"on":"2026-11-11"}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"example":{"verification":"V-0123456","action":"terminate","from":"Verified","to":"Terminated","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/verifications/{ref}/transfer":{"post":{"tags":["Writes"],"operationId":"transferVerification","summary":"Mark a verification transferred","description":"To another TA. `on` defaults to today.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference","schema":{"type":"string"},"example":"V-0123456"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"on":{"type":"string","format":"date"}}},"example":{"on":"2026-11-11"}}}},"responses":{"200":{"description":"Done","content":{"application/json":{"example":{"verification":"V-0123456","action":"transfer","from":"Verified","to":"Transferred","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/verifications/{ref}/cancel":{"post":{"tags":["Writes"],"operationId":"cancelVerification","summary":"Cancel a verification","description":"For a cycle made by mistake.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference","schema":{"type":"string"},"example":"V-0123456"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"responses":{"200":{"description":"Done","content":{"application/json":{"example":{"verification":"V-0123456","action":"cancel","from":"Verified","to":"Canceled","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/verifications/{ref}/renew":{"post":{"tags":["Writes"],"operationId":"renewVerification","summary":"Renew a verification","description":"The next cycle, with the product, participant, brand, scope and risk carried over, and this cycle's active facilities added to it (v1's `is_renewal` did nothing).","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"ref","in":"path","required":true,"description":"The `V-` reference","schema":{"type":"string"},"example":"V-0123456"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["valid_from","valid_to"],"properties":{"scope":{"type":"string"},"evaluation_started_on":{"type":"string","format":"date"},"completed_on":{"type":"string","format":"date","description":"More than 7 days back is accepted with a warning"},"valid_from":{"type":"string","format":"date"},"valid_to":{"type":"string","format":"date"},"countries_distributed":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$"},"description":"ISO 3166 codes"},"high_risk":{"type":"boolean"},"monitored_risk":{"type":"boolean"}}},"example":{"valid_from":"2027-03-01","valid_to":"2028-02-29","completed_on":"2027-02-27"}}}},"responses":{"201":{"description":"Renewed","content":{"application/json":{"example":{"verification":"V-0123999","renews":"V-0123456","facilities_carried_over":2,"result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/clients/{id}/withdraw":{"post":{"tags":["Writes"],"operationId":"withdrawClient","summary":"Withdraw a client","security":[{"bearerAuth":[]},{"oauth2":["client.write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"on":{"type":"string","format":"date"},"reason":{"type":"string"}}},"example":{"on":"2026-11-11","reason":"Left the program"}}}},"responses":{"200":{"description":"Withdrawn","content":{"application/json":{"example":{"client":"12345","status":"withdrawn","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/products/{id}/countries-of-origin":{"get":{"tags":["Verification management"],"operationId":"listCountriesOfOrigin","summary":"List a product's countries of origin","description":"Where the product's inputs come from, by quarter, with their eligibility level once reviewed.","security":[{"bearerAuth":[]},{"oauth2":["verification.read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"TA product ID or FIP-OS id","schema":{"type":"string"},"example":"2-12345-00001"}],"responses":{"200":{"description":"Countries of origin","content":{"application/json":{"example":{"data":[{"input_ingredient":"Corn starch","country":"United States","crop_source":"Corn","eligibility_level":"Eligible","quarter":"Q3","year":"2026","approved_on":"2026-10-02"}]}}}},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Writes"],"operationId":"addCountryOfOrigin","summary":"Add a country of origin","description":"Where one of the product's inputs comes from, for a quarter (was v1's missing /coo). `country` is an ISO 3166 code or the country's name.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"TA product ID or FIP-OS id","schema":{"type":"string"},"example":"2-12345-00001"},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["input_ingredient","country","quarter","year"],"properties":{"input_ingredient":{"type":"string"},"country":{"type":"string"},"crop_source":{"type":"string"},"quarter":{"type":"string","enum":["Q1","Q2","Q3","Q4"]},"year":{"type":"string","pattern":"^20\\d\\d$"}}},"example":{"input_ingredient":"Corn starch","country":"US","crop_source":"Corn","quarter":"Q4","year":"2026"}}}},"responses":{"201":{"description":"Added","content":{"application/json":{"example":{"product":"2-12345-00001","country":"United States","quarter":"Q4","year":"2026","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/clients/{id}/hold-all":{"post":{"tags":["Writes"],"operationId":"holdAllForClient","summary":"Put all a client's products on hold","description":"Every verified product of the client with your TA (or only one program's), each answered separately.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Client id or number","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"on":{"type":"string","format":"date"},"program":{"type":"string","enum":["non_gmo","non_upf"]}}},"example":{"on":"2026-11-12"}}}},"responses":{"200":{"description":"Each verification's result","content":{"application/json":{"example":{"client":"12345","action":"hold_all","verifications":2,"failed":0,"results":[{"verification":"V-0123456","status":200,"result":"saved"}]}}}},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/clients/{id}/withdraw-all":{"post":{"tags":["Writes"],"operationId":"withdrawAllForClient","summary":"Withdraw all a client's products","description":"Every verified or on-hold product of the client with your TA (or only one program's), with one reason; each answered separately.","security":[{"bearerAuth":[]},{"oauth2":["verification.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Client id or number","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"properties":{"requested_on":{"type":"string","format":"date"},"reason":{"type":"string","enum":["Cost Prohibitive","Unable to Achieve Verification","Lack of Value of Verification","Non-Responsive","Discontinued Product","Unresolved Major Non-Conformity","Willful Lying/Falsification","NGP Product License Termination","NGP Termination of License Agreement","Other","Plans to Re-enroll","Lost Buyer"]},"program":{"type":"string","enum":["non_gmo","non_upf"]}}},"example":{"requested_on":"2026-11-12","reason":"Plans to Re-enroll"}}}},"responses":{"200":{"description":"Each verification's result","content":{"application/json":{"example":{"client":"12345","action":"withdraw_all","verifications":3,"failed":0,"results":[{"verification":"V-0123456","status":200,"result":"saved"}]}}}},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/clients/{id}/contacts/{role}":{"put":{"tags":["Writes"],"operationId":"putClientContact","summary":"Set a client's contact","description":"The main, secondary or marketing contact (was v1's missing /contact).","security":[{"bearerAuth":[]},{"oauth2":["client.write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"role","in":"path","required":true,"schema":{"type":"string","enum":["main","secondary","marketing"]}},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":"string"},"title":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string","format":"email"}}},"example":{"first_name":"Alex","last_name":"Example","email":"alex@example.com"}}}},"responses":{"200":{"description":"Set","content":{"application/json":{"example":{"client":"12345","role":"secondary","result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Writes"],"operationId":"removeClientContact","summary":"Clear a client's contact","security":[{"bearerAuth":[]},{"oauth2":["client.write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"role","in":"path","required":true,"schema":{"type":"string","enum":["main","secondary","marketing"]}},{"name":"Idempotency-Key","in":"header","description":"Any unique text (a UUID is good). Sent again within 24 hours with the same request, you get the first answer back instead of a second change.","schema":{"type":"string","maxLength":255}},{"name":"If-Match","in":"header","description":"The ETag from your last GET of this resource: the change applies only if it hasn't changed since.","schema":{"type":"string"}}],"responses":{"200":{"description":"Cleared","content":{"application/json":{"example":{"client":"12345","role":"marketing","removed":true,"result":"saved","note":"Saved. The read endpoints show it within about five minutes."}}}},"202":{"description":"Accepted; Salesforce didn't answer in time, so it's sent again within two minutes (`result: queued`)"},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/batch":{"post":{"tags":["Writes"],"operationId":"batch","summary":"Send up to 500 operations","description":"Each operation runs as you, in order, and gets its own answer (207). One failing doesn't stop the others; each may carry its own `idempotency_key`. Single requests stay atomic.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["operations"],"properties":{"operations":{"type":"array","maxItems":500,"items":{"type":"object","required":["method","path"],"properties":{"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"]},"path":{"type":"string","description":"After /v2, like /products/2-12345-00001"},"body":{"type":"object"},"idempotency_key":{"type":"string"}}}}}},"example":{"operations":[{"method":"POST","path":"/products/2-12345-00001/packages","body":{"code":"012345678905","brand_number":"123456"},"idempotency_key":"pkg-012345678905"},{"method":"POST","path":"/verifications/V-0123456/hold","body":{"on":"2026-11-11"}}]}}}},"responses":{"207":{"description":"One result per operation","content":{"application/json":{"example":{"results":[{"index":0,"status":201,"body":{"package_number":"PKG-0098765","result":"saved"}},{"index":1,"status":200,"body":{"verification":"V-0123456","action":"hold","result":"saved"}}]}}}},"400":{"description":"The request isn't valid (`invalid_request`, with the field). Unknown fields are refused, never ignored","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The token is missing, expired or revoked (`unauthorized`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The token lacks the scope (`insufficient_scope`), or production changes need a TA's key (`ta_key_required`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Not found, or not your TA's (`not_found`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"An earlier change to this record is still on its way (`change_in_progress`), or an Idempotency-Key is still running","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"412":{"description":"The record changed since you read it: If-Match didn't match (`precondition_failed`), or Salesforce had a newer version (`changed_since`, with `current`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Salesforce refused it (`salesforce_refused`, with its reason), or an Idempotency-Key was reused for a different request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests (`rate_limited`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Changes like this aren't switched on yet (`writes_not_switched_on`)","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A token: your explorer token on the developer site, a personal token, or one from POST /v2/oauth/token."},"oauth2":{"type":"oauth2","description":"Client credentials for an organization's key.","flows":{"clientCredentials":{"tokenUrl":"https://fip-os-api.vercel.app/v2/oauth/token","scopes":{"catalog.read":"Listed products, brands, categories, the feed and export","catalog.read.extended":"The token-only fields (package details, other certifications)","verification.read":"The TA's clients, products, packages, verifications, facilities, certificates","verification.write":"Create and update them, and the verification actions","client.write":"Applications, client details, brands and owners","prospect.read":"Opportunities shared with the TA","prospect.write":"Claiming and updating them","certificate.issue":"Certificate PDFs","webhook.manage":"Webhook subscriptions"}}}}},"schemas":{"Problem":{"type":"object","description":"RFC 9457 problem details","properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"code":{"type":"string"},"detail":{"type":"string"},"field":{"type":"string"},"instance":{"type":"string"},"request_id":{"type":"string"}},"required":["type","title","status","code"]},"Token":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","enum":["Bearer"]},"expires_in":{"type":"integer"},"scope":{"type":"string"}}},"Verification":{"type":"object","properties":{"program":{"type":"string","enum":["non_gmo","non_upf"]},"status":{"type":"string"},"certificate_number":{"type":"string"},"valid_from":{"type":"string","format":"date"},"valid_to":{"type":"string","format":"date"},"scope":{"type":["string","null"]},"risk_status":{"type":["string","null"]},"technical_administrator":{"type":"string"},"listed":{"type":"boolean"}}},"Product":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"ta_product_id":{"type":["string","null"]},"single_ingredient":{"type":"boolean"},"brand":{"type":["object","null"],"properties":{"number":{"type":"string"},"name":{"type":"string"}}},"participant":{"type":["string","null"]},"verifications":{"type":"array","items":{"$ref":"#/components/schemas/Verification"}},"categories":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"group":{"type":"string"},"primary":{"type":"boolean"}}}},"gtins":{"type":"array","items":{"type":"string"}},"packages":{"type":"array","description":"With catalog.read.extended","items":{"type":"object"}},"other_certifications":{"type":"array","description":"With catalog.read.extended","items":{"type":"object"}},"updated_at":{"type":"string","format":"date-time"}}},"ProductPage":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"next_cursor":{"type":["string","null"]}}}}}}