resemble-detect

Checks if audio, images, video, or text were made by AI to spot deepfakes and fake content.

Installation
Run `npx skills add "https://github.com/resemble-ai/detect-skill" --skill "resemble-detect"` to install this skill, then follow its SKILL.md instructions for my next request.

Paste this into Claude Code, Cursor, or any agent that can run commands.

SKILL.mdShow the author's original SKILL.md
---
name: resemble-detect
description: Deepfake detection and media safety — detect AI-generated audio, images, video, and text, trace synthesis sources, and analyze media intelligence using direct Resemble AI API calls
---

# Resemble Detect — Deepfake Detection & Media Safety

Analyze audio, image, video, and text for synthetic manipulation, AI-generated content, and media intelligence using **direct Resemble AI API calls**.

## Core Principle — THE IRON LAW

**"NEVER DECLARE MEDIA AS REAL OR FAKE WITHOUT A COMPLETED DETECTION RESULT."**

Do not guess, infer, or speculate about media authenticity. Every authenticity claim must be backed by a completed Resemble Detect job with a returned `label`, `score`, and `status: "completed"`. If the detection is still `processing`, wait. If it `failed`, say so — do not substitute your own judgment.

The same law applies to text. Never call writing AI-generated or human-written from style, tone, or "it reads like ChatGPT." A text verdict requires a completed `POST /text_detect` job with `prediction`, `confidence`, and `status: "completed"`.

## When to Use

Use this skill whenever the user's request involves any of these:

- Checking if audio, video, or image is AI-generated or manipulated
- Checking if text — an essay, email, post, review, article, cover letter, or comment — was written by an AI model ("slop detection")
- Detecting deepfakes in any media format
- Verifying media authenticity or provenance
- Identifying which AI platform synthesized audio (source tracing)
- Analyzing media for speaker info, emotion, transcription, or misinformation
- Asking natural-language questions about detection results
- Running a full investigation workflow — insurance claim, breaking news, ID check, submitted evidence — where detection is one input among several
- Any mention of: "deepfake", "fake detection", "synthetic media", "media forensics", "authenticity check", "source tracing", "is this real", "AI-written", "written by ChatGPT", "AI text", "slop", "is this human-written"

**Do NOT use** for text-to-speech generation, voice cloning, or speech-to-text transcription — those are separate Resemble capabilities. (Detecting whether *text* is AI-written **is** in scope — see Phase 5.)

## Required Setup

- **API key:** Bearer token from the Resemble dashboard: <https://app.resemble.ai/account/api>
- **Environment variable:** prefer `RESEMBLE_API_KEY`
- **Base URL:** `https://app.resemble.ai/api/v2`
- **Auth header:** `Authorization: Bearer $RESEMBLE_API_KEY`
- **Media inputs:** `POST /detect` accepts exactly one of:
  - direct `multipart/form-data` file upload as `file` (up to 150 MB),
  - public HTTPS `url`, or
  - `media_token` from `POST /secure_uploads`.
- **Text input:** `POST /text_detect` takes a JSON body with a `text` string — at least 25 words, at most 100,000 characters. No file or URL.

Never print API keys or paste bearer tokens into chat. Use environment variables in examples and commands.

## Capability Decision Tree

| User wants to...                                      | Use this                  | API endpoint               |
|-------------------------------------------------------|---------------------------|----------------------------|
| Check if media is AI-generated / deepfake             | **Deepfake Detection**    | `POST /detect`, then `GET /detect/{uuid}` |
| Upload a private/local file without public hosting    | **Direct Upload**         | `POST /detect` multipart `file=@...` |
| Analyze a file larger than 150 MB without public URL  | **Secure Upload**         | `POST /secure_uploads`, then `POST /detect` with `media_token` |
| Know *which AI platform* made fake audio              | **Audio Source Tracing**  | `POST /detect` with `audio_source_tracing: true` |
| Get speaker info, emotion, transcription from media   | **Intelligence**          | `POST /intelligence`       |
| Ask questions about a completed detection             | **Detect Intelligence**   | `POST /detects/{uuid}/intelligence`, then poll answer |
| Run a managed multi-step investigation with a verdict | **Detect Agents**         | `GET /agents`, then `POST /agents/{preset_id}/run` (SSE) |
| Check if text was written by an AI model              | **Text Detection**        | `POST /text_detect`, then `GET /text_detect/{uuid}` |

When multiple media capabilities apply, combine them in a single `POST /detect` call using flags such as `intelligence: true`, `audio_source_tracing: true`, `visualize: true`, `use_reverse_search: true`, and `zero_retention_mode: true` instead of making separate jobs. Text detection is a separate endpoint and cannot be combined with a media detection.

## Direct API Call Rules

1. **Use direct HTTP requests first.** This skill is intentionally written around `curl` and the Resemble REST API, not MCP tool calls.
2. **Use `Prefer: wait` when a synchronous result is acceptable.** Without it, submit the job, capture the returned UUID, and poll.
3. **Poll async jobs until terminal status.** Terminal statuses are `completed` and `failed`.
4. **Use zero retention for sensitive media.** Set `zero_retention_mode: true` for media detection when privacy matters.
5. **Only report completed results.** Pending/processing jobs are not verdicts.

## Reusable Shell Setup

Use this at the start of any command sequence:

```bash
: "${RESEMBLE_API_KEY:?Set RESEMBLE_API_KEY first}"
BASE_URL="https://app.resemble.ai/api/v2"
AUTH_HEADER="Authorization: Bearer ${RESEMBLE_API_KEY}"
```

If you need JSON extraction and `jq` is available, use it. If not, use `python3 -c 'import json,sys; ...'`.

---

## Phase 1: Deepfake Detection

Submit any audio, image, or video for AI-generated content analysis.

### Submit a Detection from a Public URL

Use this when the media is already reachable via HTTPS:

```bash
curl --request POST "${BASE_URL}/detect" \
  -H "$AUTH_HEADER" \
  -H "Prefer: wait" \
  -H "Content-Type: application/json" \
  --data '{
    "url": "https://example.com/media.mp4",
    "visualize": true,
    "intelligence": true,
    "audio_source_tracing": true,
    "use_reverse_search": true,
    "zero_retention_mode": true
  }'
```

For asynchronous mode, omit `Prefer: wait`, capture `.item.uuid`, then poll `GET /detect/{uuid}`.

### Submit a Detection from a Local File

Direct file uploads are supported for files up to 150 MB:

```bash
curl --request POST "${BASE_URL}/detect" \
  -H "$AUTH_HEADER" \
  -H "Prefer: wait" \
  -F "file=@/path/to/media.mp4" \
  -F "intelligence=true" \
  -F "visualize=true" \
  -F "audio_source_tracing=true" \
  -F "frame_length=2"
```

Allowed direct-upload extensions include `.wav`, `.mp3`, `.m4a`, `.ogg`, `.aac`, `.flac`, `.amr`, `.3gp`, `.3gpp`, `.mp4`, `.mov`, `.avi`, `.mkv`, `.webm`, `.jpg`, `.jpeg`, `.png`, `.gif`, and `.webp`.

### Submit a Detection with a Secure Upload Token

Use secure uploads when the file is larger than 150 MB or should not be hosted publicly. First upload the file:

```bash
curl --request POST "${BASE_URL}/secure_uploads" \
  -H "$AUTH_HEADER" \
  -F "file=@/path/to/media.mp4"
```

Then submit the returned token as `media_token`:

```bash
curl --request POST "${BASE_URL}/detect" \
  -H "$AUTH_HEADER" \
  -H "Content-Type: application/json" \
  --data '{
    "media_token": "SECURE_UPLOAD_TOKEN",
    "intelligence": true,
    "visualize": true,
    "zero_retention_mode": true
  }'
```

Secure upload tokens are short-lived. Use them promptly.

### Detection Parameters

| Parameter              | Type    | Required | Description                                              |
|------------------------|---------|----------|----------------------------------------------------------|
| `file`                 | file    | One of   | Multipart file upload, max 150 MB                        |
| `url`                  | string  | One of   | Public HTTPS URL to audio, image, or video file          |
| `media_token`          | string  | One of   | Token from `POST /secure_uploads`                        |
| `callback_url`         | string  | No       | Webhook URL for async completion notification             |
| `visualize`            | boolean | No       | Generate heatmap/treeview visualization artifacts         |
| `intelligence`         | boolean | No       | Run multimodal intelligence analysis alongside detection  |
| `audio_source_tracing` | boolean | No       | Identify which AI platform synthesized fake audio         |
| `frame_length`         | integer | No       | Audio/video analysis window size in seconds (1–4, default 2) |
| `start_region`         | number  | No       | Start of segment to analyze (seconds)                    |
| `end_region`           | number  | No       | End of segment to analyze (seconds)                      |
| `max_video_secs`       | number  | No       | Cap processed video duration                             |
| `model_types`          | string  | No       | `"image"` or `"talking_head"` for video face-swap detection |
| `use_reverse_search`   | boolean | No       | Enable reverse image search (image only)                 |
| `use_ood_detector`     | boolean | No       | Enable out-of-distribution detection                     |
| `zero_retention_mode`  | boolean | No       | Auto-delete submitted media after detection completes    |

Exactly one of `file`, `url`, or `media_token` must be supplied.

### Poll for Detection Results

```bash
DETECT_UUID="..."
curl --request GET "${BASE_URL}/detect/${DETECT_UUID}" \
  -H "$AUTH_HEADER"
```

Polling best practice: start at 2-second intervals, back off to 5 seconds, then 10 seconds. Stop when `item.status` is `completed` or `failed`.

If you need a small polling helper:

```bash
DETECT_UUID="..."
for delay in 2 2 5 5 10 10 10 10 10 10; do
  response=$(curl -sS "${BASE_URL}/detect/${DETECT_UUID}" -H "$AUTH_HEADER")
  printf '%s\n' "$response"
  status=$(printf '%s' "$response" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("item",{}).get("status",""))')
  [ "$status" = "completed" ] && break
  [ "$status" = "failed" ] && break
  sleep "$delay"
done
```

### Reading Results by Media Type

**Audio results** — in `item.metrics`:

```json
{
  "label": "fake",
  "score": ["0.92", "0.88", "0.95"],
  "consistency": "0.91",
  "aggregated_score": "0.92",
  "image": "https://..."
}
```

- `label`: `"fake"` or `"real"` — the verdict
- `score`: per-chunk prediction scores
- `aggregated_score`: overall confidence (0.0–1.0, higher = more likely synthetic)
- `consistency`: how consistent the prediction is across chunks
- `image`: visualization heatmap URL if `visualize: true`

**Image results** — in `item.image_metrics`:

```json
{
  "type": "FinalResult",
  "label": "Fake",
  "score": 0.87,
  "image": "https://...",
  "ifl": { "score": 0.82, "heatmap": "https://..." },
  "reverse_image_search_sources": [
    { "url": "...", "title": "...", "verdict": "known_fake", "similarity": 0.95 }
  ]
}
```

**Video results** — in `item.video_metrics`, with audio metrics in `item.metrics` when the video has audio:

```json
{
  "label": "Fake",
  "score": 0.89,
  "certainty": 0.91,
  "treeview": "https://...",
  "children": [
    {
      "type": "VideoResult",
      "conclusion": "Fake",
      "score": 0.89,
      "timestamp": 2.5,
      "children": []
    }
  ]
}
```

### Interpreting Scores

| Score Range | Interpretation                                      |
|-------------|-----------------------------------------------------|
| 0.0 – 0.3   | Strong indication of authentic/real media           |
| 0.3 – 0.5   | Inconclusive — recommend additional analysis        |
| 0.5 – 0.7   | Likely synthetic — flag for review                  |
| 0.7 – 1.0   | High confidence synthetic/AI-generated              |

Always present scores with context. Say "The detection returned a score of 0.87, indicating high confidence that this media is AI-generated" — never just "it's fake."

---

## Phase 2: Intelligence — Media Analysis

Analyze media for rich structured insights independently or alongside detection.

### Standalone Intelligence

```bash
curl --request POST "${BASE_URL}/intelligence" \
  -H "$AUTH_HEADER" \
  -H "Content-Type: application/json" \
  --data '{
    "url": "https://example.com/audio.mp3",
    "media_type": "audio"
  }'
```

By default, `POST /intelligence` is synchronous. If you provide `callback_url`, it becomes asynchronous and returns an intelligence record that you can poll with `GET /intelligences/{uuid}`.

**Parameters:**

| Parameter      | Type   | Required | Description                                              |
|----------------|--------|----------|----------------------------------------------------------|
| `url`          | string | One of   | HTTPS URL to media file                                  |
| `media_token`  | string | One of   | Token from secure upload                                 |
| `detect_id`    | string | No       | UUID of existing detect to associate                     |
| `media_type`   | string | No       | `"audio"`, `"video"`, or `"image"` (auto-detected if omitted) |
| `callback_url` | string | No       | Webhook for async completion                             |

**Audio/video intelligence may include:** speaker info, language/dialect, emotion, speaking style, context, message summary, abnormalities, transcription, translation, and misinformation analysis.

**Image intelligence may include:** scene description, subjects, authenticity analysis, context/setting, abnormalities, and misinformation analysis.

### Get Intelligence

```bash
INTELLIGENCE_UUID="..."
curl --request GET "${BASE_URL}/intelligences/${INTELLIGENCE_UUID}" \
  -H "$AUTH_HEADER"
```

### Detect Intelligence — Ask Questions About Completed Detections

After a detection completes, submit natural-language questions about it:

```bash
DETECT_UUID="..."
curl --request POST "${BASE_URL}/detects/${DETECT_UUID}/intelligence" \
  -H "$AUTH_HEADER" \
  -H "Content-Type: application/json" \
  --data '{"query": "Summarize the detection results in plain language."}'
```

This returns a question UUID. Poll until the question status is `completed` or `failed`:

```bash
QUESTION_UUID="..."
curl --request GET "${BASE_URL}/detects/${DETECT_UUID}/intelligence/${QUESTION_UUID}" \
  -H "$AUTH_HEADER"
```

Good questions to suggest:

- "Summarize the detection results in plain language."
- "What specific indicators suggest this is AI-generated?"
- "How do the audio and video detection results differ?"
- "What is the confidence level and what does it mean?"
- "Are there any inconsistencies in the analysis?"

Prerequisite: the detection must have `status: "completed"`. Asking about a processing or failed detection can return `422`.

---

## Phase 3: Audio Source Tracing

When audio is detected as synthetic, identify which AI platform generated it.

Enable it in the `POST /detect` request:

```json
{
  "url": "https://example.com/audio.wav",
  "audio_source_tracing": true
}
```

Result appears in the detection response under `item.audio_source_tracing`:

```json
{
  "label": "elevenlabs",
  "error_message": null
}
```

Known source labels include `resemble_ai`, `elevenlabs`, `real`, and others as the model expands.

Standalone lookup endpoints:

```bash
curl --request GET "${BASE_URL}/audio_source_tracings" -H "$AUTH_HEADER"
curl --request GET "${BASE_URL}/audio_source_tracings/${TRACE_UUID}" -H "$AUTH_HEADER"
```

Important: source tracing is most useful when audio is labeled `fake`. If the audio is `real`, a source tracing result may be absent or identify the media as real.

---

## Phase 4: Detect Agents

Detect Agents are six Resemble-managed investigators that wrap a detection in a multi-step workflow: they run Detect, pull in supporting evidence and web research, and end on a written assessment. Use one when the question is "should this claim/post/document be trusted?" rather than "is this file synthetic?".

| Agent | `preset_id` |
|-------|-------------|
| Investigate Social Media Content | `investigate_social_content` |
| Review an Insurance Claim        | `review_insurance_claim`     |
| Verify Breaking News Media       | `verify_breaking_news`       |
| Verify a Document or Receipt     | `verify_document`            |
| Verify Submitted Evidence        | `verify_evidence`            |
| Verify an ID                     | `verify_id`                  |

List them (the `uuid` is the same stable identifier as `preset_id`):

```bash
curl --request GET "${BASE_URL}/agents" -H "$AUTH_HEADER"
```

### Run an Investigation

`multipart/form-data`, with exactly one primary media source (`file` or `url`). The response is a Server-Sent Events stream, so pass `--no-buffer`:

```bash
curl --no-buffer --request POST "${BASE_URL}/agents/verify_document/run" \
  -H "$AUTH_HEADER" \
  -H "Accept: text/event-stream" \
  -F "file=@/path/to/receipt.pdf" \
  -F "query=Is this receipt genuine?" \
  -F "evidence[]=@/path/to/order-confirmation.png" \
  -F "check_urls=https://example.com/original-listing"
```

| Field        | Required    | Description                                          |
|--------------|-------------|------------------------------------------------------|
| `file`       | One of      | Media to analyze                                     |
| `url`        | One of      | Public HTTPS media URL                               |
| `query`      | No          | The investigation question or objective              |
| `evidence[]` | No          | Supporting files; repeat the field for multiple      |
| `check_urls` | No          | Additional URLs for the agent to check               |

### Reading the Stream

Each frame is `data: {json}`. The frames that matter:

- `run_started` — carries `run_id`. Save it; the run is persisted server-side even if you disconnect.
- `detect` — the Resemble Detect evidence, with `label` and `score`. **This is the authenticity verdict under the Iron Law.**
- `gate` — whether the investigative agent proceeded past detection (`agent_ran`).
- `tool_call` / `tool_result` — research and detection steps as they happen.
- `token` / `agent_message` / `message_end` — incremental agent narration.
- `final_verdict` — the written assessment. `intelligence` is a string, and may contain serialized JSON when the agent uses a schema.
- `done` — completed. `error` — failed after the stream opened (HTTP stays `200`).

**Iron Law applies unchanged.** `final_verdict` is the agent's reasoning, not a detection result. Never report media as real or fake on the strength of the narration alone — cite the `label` and `score` from the `detect` frame. If no `detect` frame arrived, say the detection did not complete.

### Retrieve Past Runs

```bash
curl --request GET "${BASE_URL}/agents/verify_document/runs" -H "$AUTH_HEADER"
curl --request GET "${BASE_URL}/agents/verify_document/runs/${RUN_ID}" -H "$AUTH_HEADER"
```

Run detail adds the full event transcript, the config snapshot, and the agent's memory before/after — enough to replay the investigation.

### Access

Listing agents and reading run history need nothing beyond a valid API key. **Starting a run** requires entitlement: the D-Agent tier bundle on the team, or one of the team's **5 lifetime free runs**. When neither applies the run endpoint returns `402 Payment Required` as plain JSON *before* the stream opens — check for that before assuming a stream. `GET /agents` also returns `free_runs_remaining`, `free_runs_limit`, and `entitled`, so prefer checking those over triggering a 402.

Creating or editing agents is not available over the API; the six presets are managed by Resemble and activate on first run.

---

## Phase 5: Text Detection

Detect whether a piece of writing was generated by an AI language model or written by a human. One endpoint, one text per call, JSON in and JSON out.

> **Access:** available to teams on a current billing plan (billed as one Text Detection unit per completed job) and to `detect_beta_user` accounts. Accounts without access receive a `400` with `"This feature is not available for your account"`; teams that are out of entitlement receive a `402`.

### Before You Call — Count the Words

The detector does not score text under **25 words**. Below that length no model or threshold separates casual human writing from AI text, so the API rejects the request with a `400` rather than returning a guess. Check first so you never send a request that can only fail:

```bash
TEXT="$(cat /path/to/text.txt)"
WORDS=$(printf '%s' "$TEXT" | wc -w | tr -d ' ')
if [ "$WORDS" -lt 25 ]; then
  echo "Only ${WORDS} words — the detector needs at least 25. Not enough text to judge."
fi
```

If the user has several short messages from the same author (chat, tweets, comments), concatenate them into one request of 25+ words. That is the supported way to get a verdict on short-form writing — say clearly that the verdict covers the combined text.

The model reads roughly the first **350–400 words** (512 tokens). Longer text is scored on its beginning. For whole-document coverage, split into ~300-word chunks, submit each, and report the per-chunk results rather than a single number.

### Submit a Text Detection

```bash
curl --request POST "${BASE_URL}/text_detect" \
  -H "$AUTH_HEADER" \
  -H "Prefer: wait" \
  -H "Content-Type: application/json" \
  --max-time 320 \
  --data "$(python3 -c 'import json,sys; print(json.dumps({"text": sys.stdin.read()}))' < /path/to/text.txt)"
```

Build the JSON body with `jq -n --arg text "$TEXT" '{text: $text}'` or the `python3` one-liner above — never paste raw text into a hand-written JSON string, since quotes and newlines will break the request.

**Parameters:**

| Parameter             | Type    | Required | Description                                                                                  |
|-----------------------|---------|----------|----------------------------------------------------------------------------------------------|
| `text`                | string  | Yes      | The text to analyze. At least 25 words, at most 100,000 characters. Send it as-is; the server normalizes unicode, invisible characters, and curly quotes. |
| `threshold`           | float   | No       | Decision cutoff 0.0–1.0 on the model's AI probability (default `0.5`). Leave the default unless the user has measured a different operating point. |
| `thinking`            | string  | No       | `"low"` (default), `"medium"`, or `"high"`. Leave at `"low"`.                                 |
| `callback_url`        | string  | No       | HTTPS webhook called with `{ "success": true, "item": {...} }` on completion, or `{ "success": false, "item": {...}, "error": "..." }` on failure. |
| `zero_retention_mode` | boolean | No       | If `true`, the submitted text is not stored and `text_content` is omitted from responses. `privacy_mode` is accepted as an alias. |

### Synchronous vs. Asynchronous — This Endpoint Is Different

`Prefer: wait` runs inference **inline** and returns the finished item in the same response. Warm requests take about 2 seconds, but the text model scales to zero when idle and the **first request after idle can take 3–4 minutes** while it loads. That is why the example uses `--max-time 320`. Do not treat a long wait as a hang; wait it out once, and only retry after a real timeout or a `5xx`.

Without `Prefer: wait`, the response comes back immediately with `status: "processing"`. Poll `GET /text_detect/{uuid}` with the same 2s → 5s → 10s backoff used for media, but be prepared for the first poll cycle to last several minutes on a cold start. **Do not give up after the ten-iteration helper** if the status is still `processing` — extend the loop.

```bash
TEXT_UUID="..."
curl --request GET "${BASE_URL}/text_detect/${TEXT_UUID}" -H "$AUTH_HEADER"
```

### Response

```json
{
  "success": true,
  "item": {
    "uuid": "8452e246-…",
    "status": "completed",
    "prediction": "ai",
    "confidence": 0.9973,
    "text_content": "…",
    "privacy_mode": false,
    "created_at": "…",
    "updated_at": "…"
  }
}
```

| Field          | Meaning                                                                                                         |
|----------------|-----------------------------------------------------------------------------------------------------------------|
| `status`       | `"processing"`, `"completed"`, or `"failed"`. Only `completed` carries a verdict.                                 |
| `prediction`   | `"ai"` or `"human"`. In rare cases `"uncertain"`, which means the model declined to score the text — report it as "not enough text to judge", never as human or AI. |
| `confidence`   | 0.0–1.0. **How sure the model is of `prediction`, not an AI probability.** `confidence: 0.97` with `prediction: "human"` means 97% confident it is human. |
| `text_content` | The submitted text, echoed back. Omitted when `zero_retention_mode` was set.                                     |

**Do not apply the media score table to `confidence`.** For text, read the two fields together:

| `prediction` | `confidence` | How to present it                                                                          |
|--------------|--------------|--------------------------------------------------------------------------------------------|
| `ai`         | ≥ 0.90       | "Resemble Detect classified this text as AI-generated with high confidence (0.97)."       |
| `ai`         | 0.50 – 0.90  | "Resemble Detect leans AI-generated but with moderate confidence (0.68); treat as a flag, not proof." |
| `human`      | ≥ 0.90       | "Resemble Detect classified this text as human-written with high confidence (0.95)."      |
| `human`      | 0.50 – 0.90  | "Resemble Detect leans human-written with moderate confidence (0.61)."                    |
| `uncertain`  | —            | "The detector could not score this text — not enough text to judge."                       |

### List Text Detections

```bash
curl --request GET "${BASE_URL}/text_detect" -H "$AUTH_HEADER"
```

Returns the team's text detections, newest first, paginated like `GET /detect`.

### Setting Expectations

Report these caveats when they apply — they are measured behaviors of the current model, not hedging:

- **Long-form** (articles, essays, blog posts, business writing, academic text): very reliable — under 0.2% of human text flagged, over 99% of AI text caught.
- **Forum and social posts**: about 2–3% of human posts are flagged as AI. Say so when the input is a Reddit-style post.
- **Casual chat just over 25 words**: about 4% false-positive rate on human text. A borderline-length casual message deserves a softer verdict.
- **Code review comments and PR descriptions**: about 5% of human write-ups are flagged. Formal technical prose is the model's weakest register on human text.
- **Short AI abstracts under 150 words**: some slip through (~85% caught).
- **Humanized or "polished" AI text**: the model is trained on it, but heavy human editing of AI output is a mixed-authorship case — describe the verdict as applying to the text as submitted.

Detection is probabilistic. A verdict is evidence, not proof of authorship, and must not be presented as grounds for an accusation or disciplinary action on its own.

---

## Recommended Workflows

### Full Media Forensics (Most Thorough)

1. Submit one `POST /detect` job with all useful flags enabled:
   ```json
   {
     "url": "https://example.com/suspect.mp4",
     "visualize": true,
     "intelligence": true,
     "audio_source_tracing": true,
     "use_reverse_search": true,
     "zero_retention_mode": true
   }
   ```
2. Poll `GET /detect/{uuid}` until `status: "completed"`.
3. Read `metrics`, `image_metrics`, or `video_metrics` for the verdict.
4. Read `intelligence.description` if intelligence was requested.
5. If audio is synthetic, check `audio_source_tracing.label` for the likely source platform.
6. Ask a follow-up via `POST /detects/{uuid}/intelligence` only after the detection is complete.

### Quick Authenticity Check (Fastest)

1. Submit minimal detection using `Prefer: wait`:
   ```bash
   curl --request POST "${BASE_URL}/detect" \
     -H "$AUTH_HEADER" \
     -H "Prefer: wait" \
     -H "Content-Type: application/json" \
     --data '{"url": "https://example.com/media.wav"}'
   ```
2. Confirm `item.status` is `completed`.
3. Check `item.metrics.label` and `item.metrics.aggregated_score` for audio, or `item.image_metrics.label` / `item.video_metrics.label` and `score` for image/video.
4. Report the result with score context and detector caveats.

### Quick AI-Text Check

1. Count words. Under 25 → stop and tell the user there is not enough text to judge (or combine several messages from the same author).
2. Submit with `Prefer: wait` and `--max-time 320`:
   ```bash
   curl --request POST "${BASE_URL}/text_detect" \
     -H "$AUTH_HEADER" \
     -H "Prefer: wait" \
     -H "Content-Type: application/json" \
     --max-time 320 \
     --data "$(jq -n --arg text "$TEXT" '{text: $text}')"
   ```
3. Confirm `item.status` is `completed`.
4. Read `item.prediction` together with `item.confidence`. Present them as a pair ("AI-generated, confidence 0.97"), name the register-specific caveat if the text is a forum post, casual chat, or code review, and remind the user the result is probabilistic.

---

## Red Flags — Stop and Reassess

- **Declaring authenticity without a completed detection result** — never say media is real or fake based on visual/auditory inspection alone.
- **Ignoring status** — `processing` is not a verdict; `failed` requires reporting the failure.
- **Ignoring score and reporting only label** — a `fake` label with score 0.51 is very different from score 0.95.
- **Submitting multiple media sources** — `file`, `url`, and `media_token` are mutually exclusive for detection.
- **Uploading files larger than 150 MB directly** — use secure upload or public URL.
- **Polling too aggressively** — start at 2 seconds and back off; do not loop at sub-second intervals.
- **Asking Detect Intelligence questions before detection completes** — this can return `422`.
- **Expecting source tracing on authentic audio** — source tracing is most useful for synthetic audio.
- **Leaking credentials** — never print bearer tokens, `.env` files, or authorization headers with real secrets.
- **Judging text by style** — "this sounds like ChatGPT" is not a verdict. Only a completed `POST /text_detect` result is.
- **Sending text under 25 words** — the API rejects it with a `400`. Count first; aggregate short messages from one author if needed.
- **Reading text `confidence` as an AI probability** — it is confidence in `prediction`. A `human` result at 0.95 is strongly human, not 95% AI.
- **Treating `uncertain` as a verdict** — it means the detector abstained. Report "not enough text to judge."
- **Timing out a text request early** — the first request after idle can take 3–4 minutes. Use `--max-time 320` and wait it out.
- **Scoring one 2,000-word document as a single number** — only the first ~350–400 words are read. Chunk it.

## Response Presentation Guidelines

When presenting results to users:

1. **Lead with the detector verdict** — "Resemble Detect classified this audio as likely AI-generated."
2. **Include status and score** — only report authenticity when status is `completed`.
3. **Name the fields used** — e.g. `item.metrics.aggregated_score`, `item.image_metrics.score`, `item.video_metrics.score`, or for text `item.prediction` with `item.confidence`.
4. **Mention limitations** — detection is probabilistic, not absolute proof or legal evidence.
5. **Include operational details** — whether intelligence, reverse search, OOD, source tracing, or zero retention was used.
6. **For inconclusive scores (0.3–0.5)** — explicitly state the result is inconclusive and recommend additional analysis or manual review.
7. **For text** — always present `prediction` and `confidence` together, name the register caveat when one applies (forum, casual chat, code review), and never present the verdict as proof of who wrote it.

## Error Handling

| Error | Cause | Resolution |
|-------|-------|------------|
| 400 | Invalid request body, missing media source, unsupported file, or multiple media sources | Check payload and supply exactly one `file`, `url`, or `media_token` |
| 400 | Text detection: fewer than 25 words, more than 100,000 characters, or `"This feature is not available for your account"` | Add text (or aggregate messages), chunk long text, or contact Resemble to enable Text Detection |
| 401 | Invalid or missing API key | Verify `RESEMBLE_API_KEY` and auth header |
| 402 | Out of entitlement for Text Detection or Detect Agents | Report the paywall to the user; do not retry |
| 404 | Detection/question/intelligence UUID not found | Verify the UUID and endpoint path |
| 422 | Detection not completed for Detect Intelligence or request validation failed | Wait for completion or fix the request |
| 429 | Rate limited | Back off and retry with exponential delay |
| 500 | Server error | Retry once, then report failure |

## Privacy & Compliance Notes

- **Zero retention mode:** set `zero_retention_mode: true` on media detection to auto-delete submitted media after analysis. Responses redact media URLs when enabled. On text detection the same flag stops the submitted text from being stored and omits `text_content` from responses.
- **Callbacks:** if using `callback_url`, ensure it is HTTPS and authenticated on your side.
- **Secrets:** keep API keys in environment variables or secret managers, never in skill files or prompts.

Ships with 2 supporting files:

  • LICENSE
  • README.md

Mirrored from the author's public source. Install counts from the open skills registry.

The systems behind these skills get built for partners every week.

Partner with us