---
title: "Link Consent"
method: POST
path: "/sms-consent/{consent_id}/link"
tags: ["sms-consent"]
---

# Link Consent

`POST /sms-consent/{consent_id}/link`

## Request

- Base URL: `https://api.verdun.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.verdun.ai/sms-consent/{consent_id}/link`
- Auth: HTTP bearer

Bind a reviewed consent to a contact slot.

Linking records consent; it does NOT lift a suppression. Surge has no
server-side re-subscribe, so a number that said STOP stays blocked until
the subscriber texts START — the response's status will say `linked` while
the number remains unsendable, and that is correct.

## Path parameters

- `consent_id` string, required

## Request body

- LinkConsentRequest: Bind a reviewed consent to a site contact slot. TWO SHAPES, EXACTLY ONE OF WHICH MUST BE USED: link to an existing contact (`site_contact_id`), or create the contact from the consent (`site_id` plus `contact`). The second writes the consent's number into the chosen slot in the same transaction — the link is a claim that this contact owns this number in this slot, so the claim and the record cannot diverge.
  - `slot` 'primary' | 'afterhours', required: Which of the contact's two numbers this consent covers.
  - `site_contact_id` string, uuid, nullable: Existing contact to bind to. Mutually exclusive with site_id/contact.
  - `site_id` string, uuid, nullable: Site to create the contact under. Requires `contact`.
  - `contact` SiteContactsModel
    - `first_name` string, required: The first name of the site contact.
    - `last_name` string, required: The last name of the site contact.
    - `role` string, required: The role of the site contact.
    - `primary_phone_number` string, required: The primary phone number of the site contact.
    - `afterhours_phone_number` string, required: The afterhours phone number of the site contact.
    - `email` string, required: The email of the site contact.
    - `notify_method` 'call' | 'text' | 'email', required
    - `availability` ContactAvailabilityWindow[], nullable: Weekly windows, in the site's timezone, when this contact is the one to reach. The review panel pre-selects the first contact on the list whose window covers the current site-local time. Null or empty means no schedule: the contact is eligible at any time and is the fallback when no scheduled contact is on. On edit, omitting the key keeps the stored schedule; null or [] clears it.
      - `days` integer[], required: Days the window starts on, Monday-first: 0 = Monday ... 6 = Sunday.
      - `start` string, required: Start time, HH:MM on a 24-hour clock, site-local.
      - `end` string, required: End time, HH:MM on a 24-hour clock, site-local; '24:00' means midnight. An end earlier than start wraps past midnight into the next day.

## Response `200`

Successful Response

- SmsConsentResponse: One consent row as the operator console sees it. Deliberately NOT the whole row: `source_ip`, `user_agent`, `phone_raw` and the raw consent text are evidence for a compliance request, not console furniture, and an endpoint that returns them invites them into a UI (and a browser cache) they have no business being in.
  - `id` string, uuid, required: Unique identifier of the consent record.
  - `phone_number` string, required: Canonical E.164 number the consent covers.
  - `full_name` string, nullable: Name given at consent time, if any.
  - `email` string, nullable: Email given at consent time, if any.
  - `organization` string, nullable: Organization given at consent time.
  - `consent_version` string, required: Server-owned key for the exact text agreed to.
  - `consented_at` string, date-time, nullable: When consent actually occurred. NULL for a Rail A enrollment that is still waiting for its YES reply.
  - `source` string, required: How the consent was obtained.
  - `status` string, required: pending_confirmation | pending_review | linked | dismissed | revoked | unverifiable. Only `linked` with a bound contact authorizes a send.
  - `site_contact_id` string, uuid, nullable: Bound contact, if any.
  - `site_id` string, uuid, nullable: Bound site, if any.
  - `contact_slot` string, nullable: primary | afterhours.
  - `evidence_ref` string, nullable: What proves it — the inbound YES message id for Rail A.
  - `linked_at` string, date-time, nullable: When it was bound to a contact.
  - `revoked_at` string, date-time, nullable: When a STOP superseded it.
  - `dismissed_at` string, date-time, nullable: When it was dismissed or expired.
  - `confirmation_sent_at` string, date-time, nullable: When the Rail A confirmation text was sent. One, ever.
  - `confirmation_error` string, nullable: Last confirmation send failure, if any.
  - `created_at` string, date-time, nullable: Row creation time.
  - `updated_at` string, date-time, nullable: Row last-update time.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/verdun/apis/verdun-api.md) - [All operations](https://skmtc.dev/verdun/apis/verdun-api/llms.txt) - [OpenAPI document](https://skmtc.dev/verdun/apis/verdun-api/revisions/35a2a6ebedda?raw)
