How to Add AI Disclosure to Your Own API ?

Since Article 50 applied on August 2, 2026, the duty to disclose sits with whoever deploys the system, and your callers can only comply if your contract tells them what they are getting. The response shape, the OpenAPI schema, and the paths where the field always disappears.

Ashley Innocent

Ashley Innocent

11 August 2026

How to Add AI Disclosure to Your Own API ?

Apidog for Enterprise

On-Premises Deploy

SSO & RBAC

SOC 2 Compliant

Explore Apidog Enterprise

Your chat UI has a little “AI-generated” badge under every response. Good. Now a partner team calls your /summarize endpoint from a batch job, writes the output into a database, and renders it inside a customer-facing report. Your badge helped nobody.

That’s the gap AI disclosure keeps falling into. Disclosure gets designed as a UI decision, so it stops at the edge of your own frontend. Machine consumers get nothing, and they’re the ones who need it most, because they can’t look at a response and tell it came from a model.

Since August 2, 2026, Article 50 of the EU AI Act has made this concrete for a lot of teams. Model providers like Anthropic mark their output at the model level, but the duty to tell people they’re dealing with AI sits with whoever deploys the system. If your API sits between the two, you’re the reason your callers can comply or can’t.

Here’s how to put disclosure in the contract rather than the interface: what to return, where to put it, how to document it, and how to test it so it survives the next refactor. Apidog covers the design, docs, and test sides of that in one place.

What goes in the response

Three things, and they answer three different questions.

Was this generated? A boolean, or better, an enum. ai_generated: true is a fine start, but generation: "synthetic" | "assisted" | "human" is more useful, because “Claude tightened a human’s draft” and “Claude wrote the whole thing” are genuinely different and Article 50’s exemptions treat them differently.

By what? Vendor and model ID. Your callers may have their own model policies, and a fallback that swaps models changes what they’re allowed to do with the output.

What do we actually know? Provenance status, if you checked. Keep this separate from the generation flag, because “we generated this” is a fact you own and “we verified a C2PA manifest” is an observation you made.

A shape that holds up:

{
  "id": "sum_4f81a2",
  "content": "The incident affected two regions for 41 minutes...",
  "ai": {
    "generation": "synthetic",
    "vendor": "anthropic",
    "model": "claude-opus-5",
    "human_review": false,
    "generated_at": "2026-08-11T09:14:22Z"
  },
  "provenance": {
    "status": "unchecked",
    "standard": null
  }
}

Two details worth defending.

human_review exists because Article 50(4) turns on it. AI-generated text published to inform the public on matters of public interest must be disclosed, unless it went through human review or editorial control with someone holding editorial responsibility. If your platform records that a human approved a draft, that fact belongs in the response, because it’s the difference between your caller needing a label and not.

provenance.status has more than two states. verified, absent, invalid, and unchecked all mean different things, and folding them into a boolean throws away the useful ones. An outage in your verification service should not look identical to a clean result.

Headers or body?

Both, for different consumers.

The body carries the truth. It’s what gets stored, logged, replayed, and passed downstream. If a caller keeps only the parsed JSON, the disclosure needs to be in there.

A header helps at the edges. A proxy, a gateway, or a logging layer that never parses your body can still route on or record a header. It’s also the only sensible place for disclosure on a response that isn’t JSON, like a plain-text or binary endpoint.

HTTP/1.1 200 OK
Content-Type: application/json
X-AI-Generated: synthetic
X-AI-Model: anthropic/claude-opus-5

Two rules for headers. Keep them consistent across every endpoint, because a header that appears on some routes is worse than one that appears on none. And never treat the header as authoritative if the body disagrees; pick one as canonical, document which, and make your tests enforce it.

For streaming responses, put the disclosure in the first event or in the response headers. A caller that starts rendering token one shouldn’t have to wait for a trailer to know what they’re rendering. If you’re new to header design generally, what are HTTP headers covers the fundamentals.

Put it in the spec

A disclosure field that isn’t in your OpenAPI definition is a convention, and conventions rot. Define it as a reusable schema so every AI-backed endpoint uses the same shape:

components:
  schemas:
    AiDisclosure:
      type: object
      required: [generation]
      properties:
        generation:
          type: string
          enum: [synthetic, assisted, human]
          description: >
            synthetic = produced by a model with no human authoring.
            assisted = a human authored the content and a model edited,
            translated, or summarised it.
            human = no model involvement.
        vendor:
          type: string
          example: anthropic
        model:
          type: string
          example: claude-opus-5
        human_review:
          type: boolean
          description: >
            True when a person reviewed the output before it was returned
            and an identifiable party holds editorial responsibility.
        generated_at:
          type: string
          format: date-time

Then reference it everywhere, and make ai a required property on any response that can contain model output. Required matters: an optional field is one that a caller has to write defensive code for, and most won’t.

Two knock-on benefits. Your generated documentation now explains the field to every consumer without anyone writing a wiki page. And spec validation will catch the day it disappears, which is the failure mode that actually happens. How to validate OpenAPI specs covers the validation side, and OpenAPI diff to block breaking changes in CI will catch someone quietly making it optional.

The paths people forget

Disclosure fields go missing on the routes nobody thinks about. Four to check explicitly.

Cached responses. A cache layer that stores the body before the disclosure is attached will serve unmarked output for as long as the TTL lasts. Cache the complete response, not the model output plus a wrapper you rebuild.

Error and partial responses. A timeout that returns a partial summary is still returning model output. If your error envelope has a different shape, it needs the field too.

Batch and webhook payloads. Async delivery often uses a slimmer schema built by different code. This is the single most common place the field is missing.

Fallback paths. When the primary model fails and you fall back, the model value must follow. A hardcoded model string in a disclosure block is a lie waiting to happen.

The fix for all four is the same: attach disclosure at the point where model output enters your response object, not at the point where you serialize the happy path.

Test it like a guarantee

A disclosure field is a promise to your callers. Promises that aren’t tested are documentation.

Five assertions cover most of it, and they’re ordinary API tests.

1. The field exists on every AI-backed route.

const body = pm.response.json();
pm.test("response carries AI disclosure", function () {
    pm.expect(body).to.have.property("ai");
    pm.expect(body.ai.generation).to.be.oneOf(["synthetic", "assisted", "human"]);
});

2. The header matches the body.

pm.test("header and body agree", function () {
    pm.expect(pm.response.headers.get("X-AI-Generated")).to.eql(body.ai.generation);
});

3. The reported model matches what you actually called. This is the one that catches silent fallbacks. Whether output is watermarked upstream depends on the model ID, which is why Claude’s API watermarking makes model pinning a compliance detail rather than a performance one.

4. The cached path still discloses. Call twice, assert the second response, which came from cache, has the same disclosure as the first.

5. The error path still discloses. Force a timeout or a downstream failure and assert the envelope still carries the field.

Group these into a test scenario, add schema validation against your OpenAPI definition, and run it from apidog-cli in CI:

apidog run --access-token "$APIDOG_ACCESS_TOKEN" \
  -t "$DISCLOSURE_SCENARIO_ID" -e "$APIDOG_ENV_ID" -r cli,html

It exits non-zero when an assertion fails, so a merge that drops the field fails the build instead of shipping. The full pipeline setup is in automating API tests in GitHub Actions, and general assertion patterns are in API assertions. Download Apidog to build the scenario against your own endpoints.

Document it where callers look

Two audiences, two places.

In the reference. The schema description does most of the work if you write it properly. Say what assisted means in your product, not in the abstract. A caller deciding whether they need a label is reading that sentence to make a legal decision.

In a short policy page. One page covering which endpoints can return model output, which models you use, whether human review happens and what it means when it does, and what you do and don’t guarantee. Link it from the reference. Version it.

Be specific about the limits. If you pass through Claude output, the text carries an embedded watermark you cannot verify yourself and Anthropic hasn’t opened detection. Saying so is better than implying a verification capability you don’t have. The reasoning is in how to detect Claude’s watermark.

Interactive docs help here more than usual, because a caller can see the disclosure field in a live response rather than trusting a table. Hosting interactive API docs with a try-it console covers that setup.

FAQ

Is an X-AI-Generated header a standard? No. There’s no ratified standard header for AI disclosure. Pick a name, document it, keep it consistent, and treat it as part of your contract.

Should the disclosure be in the header or the body? Both. The body is what gets stored and passed on. The header serves proxies, gateways, logs, and non-JSON responses. Document which one is canonical if they ever disagree.

Do I legally have to do this? Depends on your role and content. Article 50 obligations fall on providers and deployers differently, and 50(4) is scoped to deepfakes and public-interest text with an exemption for human editorial control. EU AI Act Article 50 for API developers breaks it down. The legal call is your counsel’s; the plumbing is yours.

My provider already watermarks its output. Isn’t that enough? No. A watermark is a machine-readable signal your callers can’t currently read for text, and it doesn’t satisfy your own disclosure duties as a deployer. It’s a complement, not a substitute.

What about streaming responses? Put the disclosure in the response headers or the first event. Callers render as tokens arrive and shouldn’t have to wait for the end.

How do I handle content a human edited after generation? That’s what assisted and human_review are for. Article 50(4) has an exemption for content under human review with editorial responsibility, so recording it accurately is worth more than a single boolean.

Should I version this field? It’s part of your response schema, so version it exactly like the rest. Adding an enum value is a change your callers need to hear about, and a spec diff in CI will tell them.

The takeaway

AI disclosure fails as a UI feature and works as a contract. Put a required field in the response, mirror it in a header, define it once in your OpenAPI spec, and assert it on the cached, error, batch, and fallback paths where it always goes missing.

That’s maybe an afternoon of work, and it converts a claim in your marketing into something your callers can build on and your tests can enforce.

button

Explore more

Gemini 3.7 Flash Pricing Explained: Lock In Rates Before They Double

Gemini 3.7 Flash Pricing Explained: Lock In Rates Before They Double

Gemini 3.7 Flash pricing: $0.75/$3.75 per 1M tokens until Dec 31, 2026, then rates double. See worked cost examples and five ways to cut your token spend.

14 August 2026

How to Use the Gemini 3.7 Flash API ?

How to Use the Gemini 3.7 Flash API ?

Hands-on Gemini 3.7 Flash API quickstart: get a key, call the endpoint in cURL, Python, and Node.js, stream responses, and test everything in Apidog.

14 August 2026

How to Remove the Claude Watermark?

How to Remove the Claude Watermark?

Claude now embeds an invisible watermark in every text output. Here's what it actually is, what survives editing, and how to strip it with the open-source watermarks-remover tool.

13 August 2026

Practice API Design-first in Apidog

Discover an easier way to build and use APIs

How to Add AI Disclosure to Your Own API ?