---
title: "Record a corroboration"
method: POST
path: "/projects/{id}/corroborations"
tags: ["Corroborations"]
---

# Record a corroboration

`POST /projects/{id}/corroborations`

## Request

- Base URL: `https://api.epovest.com/v1`
- URL: `https://api.epovest.com/v1/projects/{id}/corroborations`
- Auth: HTTP bearer

Records one from its `url` alone, the exact page and never the home page of the site: the
`domain` and the `label` are DERIVED from the address. Record a page you have actually read:
it is a statement of fact, so confirm with the person first. Manager or above.

## Request body

- object
  - `url` string, uri, required: The exact page, absolute http(s).
  - `label` string, nullable
  - `notes` string, nullable
  - `published_on` string, date, nullable: The day the page was PUBLISHED, as `YYYY-MM-DD`, null when unknown rather than guessed.
  - `request_channel` 'available' | 'none' | 'unknown': Whether someone can be asked to change the page: `available` (a contact or a process exists), `none` (nobody to ask) or `unknown` (not filled in, the default). Three states, because "no channel" and "not filled in" are two different facts.

## Response `201`

The corroboration, recorded.

- CorroborationEnvelope
  - `corroboration` Corroboration, required
    - `id` string, uuid, required
    - `project_id` string, uuid, required
    - `url` string, required: The exact page.
    - `domain` string, required: The registrable domain the page sits on, derived from the address by the same normalizer as the Atlas. Correcting the `url` always recomputes it.
    - `label` string, nullable, required
    - `notes` string, nullable, required
    - `published_on` string, date, nullable, required: The day the page was PUBLISHED, distinct from `created_at`, the day it was recorded. It is the publication date that means something against the citation curves.
    - `author` string, nullable, required
    - `request_channel` 'available' | 'none' | 'unknown', required: Whether someone can be asked to change the page: `available` (a contact or a process exists), `none` (nobody to ask) or `unknown` (not filled in, the default). Three states, because "no channel" and "not filled in" are two different facts.
    - `monitoring` 'off' | 'daily' | 'weekly' | 'monthly', required: The cadence of the automatic watching of a corroboration. Each executed check runs the same pass as the on-demand check and debits the prepaid balance per executed check.
    - `monitoring_paused` boolean, required: True while the passes wait on the balance, the cadence kept: a top-up resumes them as they were.
    - `archived` boolean, required
    - `page_check` PageCheck, required: The latest reading of the page, refreshed by `POST /corroborations/{id}/verify` and by the watching cadence.
      - `readability` 'pending' | 'html' | 'unreadable' | 'blocked' | 'unreachable' | 'no_page', required
      - `checked_at` string, date-time, nullable, required
      - `http_status` integer, nullable, required
      - `mention` object, required: The finding on the passage around the brand: `unchanged`, `changed` (the passage moved, worth re-reading), `figures` (it moved on NUMBERS only, such as a rank or a counter, so `changed_at` stays where it was) or `gone` (the page did not answer).
        - `finding` 'unchanged' | 'changed' | 'figures' | 'gone' | 'null', nullable
        - `excerpt` string, nullable
        - `excerpt_before` string, nullable: The passage as it stood BEFORE `changed_at`, so comparing the two texts says what moved without reading the whole page again.
        - `extractor` string, nullable
        - `changed_at` string, date-time, nullable
      - `link` object, nullable, required: The finding on the links of the page towards the addresses of the brand.
        - `finding` 'unchanged' | 'changed' | 'gone' | 'none'
        - `changes` unknown[]
          - unknown
        - `outbound` integer, nullable
        - `noindex` boolean, nullable
        - `page_nofollow` boolean, nullable
        - `canonical_elsewhere` string, nullable
        - `links` LinkSighting[]
          - `url` string
          - `kind` string, nullable
          - `anchor` string, nullable
          - `rel` string[]: The rel tokens of the link, among `nofollow`, `sponsored` and `ugc`.
          - `html_target` string, nullable
          - `title` string, nullable
          - `in_main_content` boolean, nullable
          - `nature` string, nullable
          - `final_url` string, nullable
          - `hops` integer, nullable
          - `final_status` integer, nullable
          - `first_seen` string, date-time
          - `last_seen` string, date-time
          - `changed_at` string, date-time, nullable
    - `created_at` string, date-time, required

## Other responses

- `401`: `unauthenticated`: missing, unknown or revoked key.
- `403`: `insufficient_scope`: the credential may not do this, either because the key lacks the scope, or, on a one-click connection, because the person lacks the role the action needs (manager, or owner to start, pause and top up). On that path, read it as "this person may not do this" and tell them which role the action needs.
- `404`: `not_found`: unknown for this account.
- `409`: `duplicate`: this page is already in the registry of this project.
- `422`: `unplaceable_url` (the address has no registrable domain), `own_domain` (this is the own site of the brand: use `POST /projects/{id}/surfaces`), or `invalid_corroboration`.
- `429`: `rate_limited`: 120 requests per minute per credential, sliding window. Retry after `Retry-After` seconds.

---

[API](https://skmtc.dev/epovest/apis/epovest-api.md) · [All operations](https://skmtc.dev/epovest/apis/epovest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/epovest/apis/epovest-api/revisions/7a78ee27ecbb?raw)
