---
title: "Revoke Stop Request"
method: POST
path: "/crm/contacts/{contact_id}/stop-requests/{request_id}/revoke"
tags: ["authenticated", "crm"]
---

# Revoke Stop Request

`POST /crm/contacts/{contact_id}/stop-requests/{request_id}/revoke`

## Request

- Base URL: `https://api-docs.upsmith.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api-docs.upsmith.com/crm/contacts/{contact_id}/stop-requests/{request_id}/revoke`
- Auth: one of:
  - API key in header `x-api-key`
  - HTTP bearer

## Path parameters

- `contact_id` string, required
- `request_id` string, required

## Headers

- `organization-id` string, nullable
- `x-user-id` string, nullable
- `timezone` string
- `x-actor-email` string, nullable
- `x-request-source` string, nullable
- `x-operation-id` string, nullable
- `x-commit-message` string, nullable
- `x-confirmation-id` string, nullable
- `x-chat-id` string, nullable

## Request body

- RevokeStopRequestBody
  - `revoked_reason` string, required

## Response `200`

Successful Response

- ContactStopPolicyPublic
  - `do_not_contact` boolean, required
  - `requests` StopRequestPublic[], required
    - `id` string, required
    - `scope` 'customer' | 'contact_method' | 'campaign' | 'maintenance' | 'estimate' | 'membership' | 'job' | 'lead' | 'canceled_appointment' | 'marketing', required: What the customer asked us to stop. Stored as a plain string column (not a DB enum): crm is zero-ETL replicated to Redshift, which can't represent a native Postgres enum.
    - `scope_label` string, nullable, required
    - `reason` 'lead_declined' | 'lead_already_resolved' | 'customer_opt_out' | 'canceled_appointment_already_resolved' | 'office_toggle' | 'carrier_opt_out' | 'contact_deceased' | 'customer_moved' | 'not_a_fit' | 'wrong_number' | 'service_titan_customer_note' | 'service_titan_location_note' | 'service_titan_job_note' | 'service_titan_contact_memo' | 'service_titan_job_reminders_off' | 'service_titan_marketing_updates_off' | 'unknown', required: Why outreach stopped. One vocabulary across every scope. The per-table reason enums this replaced diverged mostly because they lived in different tables — a campaign opt-out and a customer-scope opt-out are one reason at two scopes, so ``scope`` carries what their values encoded. UNKNOWN covers rows predating the audit columns. It is unclassified, not reversible: a bulk lift must not clear a mark it cannot explain.
    - `origin` 'customer' | 'office' | 'carrier' | 'service_titan' | 'upsmith', required: Where the stop came from, independent of why. ``service_titan`` means we read it out of the shop's own records rather than anyone asking us — the row still stands on its own once written.
    - `customer_statement` string, nullable, required
    - `requested_by_name` string, nullable, required
    - `created_at` string, date-time, required
    - `expires_at` string, date-time, nullable, required
    - `revocable` boolean, required
    - `reached_service_titan` boolean, required
  - `external_block` ExternalBlockPublic, required: A block we did not record and cannot lift: the shop's own ServiceTitan state. A pinned note becomes a stop request only when an engagement start reads it, so a contact can be blocked by one before any row exists. Either way the office cannot clear it from here — it is theirs, in their system.
    - `source` 'service_titan_flags' | 'pinned_customer_note' | 'pinned_location_note' | 'pinned_job_note' | 'upsmith_flag' | 'stop_request' | 'all_phones_opted_out', required: Which check made a contact do-not-contact, in the order they are evaluated.
    - `label` string, required

## Other responses

- `422`: Validation Error

---

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