Hail Contacts API

The contacts API from Hail — 3 operation(s) for contacts.

GitHub OpenAPI

Operations 6

GET /v1/contacts List Contacts #
POST /v1/contacts Create Contact #
PATCH /v1/contacts/{contact_id} Patch Contact #
DELETE /v1/contacts/{contact_id} Delete Contact #
PUT /v1/members/{user_id}/phone Put Member Phone #
DELETE /v1/members/{user_id}/phone Delete Member Phone #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/hail-so:hail-so-contacts-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

hail-so-contacts-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Hail Contacts API
  description: 'Give your AI agent a voice, a real phone number, and an inbox.


    This file is the source of truth for the Go CLI. Regenerate it after

    changing API routes — see docs/public/contributing.md.'
  version: 0.1.0
servers:
- url: https://api.hail.so
  description: Hail Cloud
tags:
- name: Contacts
paths:
  /v1/contacts:
    get:
      tags:
      - Contacts
      summary: List Contacts
      description: 'List contacts for the caller''s organization, newest first.


        Merges two sources into one list: org members (kind="member", ids

        prefixed "member:", managed via membership — not editable here) and

        manually-created contacts (kind="manual", editable via PATCH/DELETE

        /contacts/{contact_id}). Cursor-paginated; q does a substring search

        over name/phone/email.'
      operationId: list_contacts_v1_contacts_get
      parameters:
      - name: q
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Q
      - name: cursor
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Cursor
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          maximum: 500
          minimum: 1
          default: 100
          title: Limit
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContactListResponse'
        '429':
          description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
            RateLimit-Limit:
              description: The request ceiling for the current window.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
    post:
      tags:
      - Contacts
      summary: Create Contact
      description: 'Create a manual contact with a phone and/or an email.


        Requires at least one of phone_e164 or email. Fails with 409 if a

        contact with the same phone or email already exists in this

        organization.'
      operationId: create_contact_v1_contacts_post
      parameters:
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactCreate'
      responses:
        '201':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContactEntry'
        '429':
          description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
            RateLimit-Limit:
              description: The request ceiling for the current window.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /v1/contacts/{contact_id}:
    patch:
      tags:
      - Contacts
      summary: Patch Contact
      description: 'Update a manual contact''s fields. Only fields present in the body change.


        Manual contacts only — a member: id (org members synced from

        membership) returns 422; edit those via the membership APIs instead.

        The contact must still have at least one of phone_e164 or email after

        the update. Fails with 409 on a duplicate phone/email.'
      operationId: patch_contact_v1_contacts__contact_id__patch
      parameters:
      - name: contact_id
        in: path
        required: true
        schema:
          type: string
          title: Contact Id
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactPatch'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContactEntry'
        '429':
          description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
            RateLimit-Limit:
              description: The request ceiling for the current window.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
    delete:
      tags:
      - Contacts
      summary: Delete Contact
      description: 'Permanently remove a manual contact.


        Manual contacts only — a member: id returns 422; org members are

        removed via the membership APIs, not this route. Irreversible.'
      operationId: delete_contact_v1_contacts__contact_id__delete
      parameters:
      - name: contact_id
        in: path
        required: true
        schema:
          type: string
          title: Contact Id
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      responses:
        '204':
          description: Successful Response
        '429':
          description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
            RateLimit-Limit:
              description: The request ceiling for the current window.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /v1/members/{user_id}/phone:
    put:
      tags:
      - Contacts
      summary: Put Member Phone
      description: 'Set an org member''s phone number.


        Pass user_id="me" to set your own, or a member''s user id — setting

        another member''s phone requires the caller to be an org owner or

        admin. Returns 404 if the target is not a member of this organization.'
      operationId: put_member_phone_v1_members__user_id__phone_put
      parameters:
      - name: user_id
        in: path
        required: true
        schema:
          type: string
          title: User Id
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MemberPhonePut'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
                title: Response Put Member Phone V1 Members  User Id  Phone Put
        '429':
          description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
            RateLimit-Limit:
              description: The request ceiling for the current window.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
    delete:
      tags:
      - Contacts
      summary: Delete Member Phone
      description: 'Clear an org member''s phone number.


        Pass user_id="me" to clear your own, or a member''s user id — clearing

        another member''s phone requires the caller to be an org owner or

        admin. Returns 404 if the target is not a member of this organization.'
      operationId: delete_member_phone_v1_members__user_id__phone_delete
      parameters:
      - name: user_id
        in: path
        required: true
        schema:
          type: string
          title: User Id
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      responses:
        '204':
          description: Successful Response
        '429':
          description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
            RateLimit-Limit:
              description: The request ceiling for the current window.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    ContactCreate:
      properties:
        name:
          type: string
          maxLength: 200
          minLength: 1
          title: Name
          description: Display name for the contact.
        phone_e164:
          anyOf:
          - type: string
          - type: 'null'
          title: Phone E164
          description: Phone number, E.164 format. At least one of phone_e164 or email is required.
        email:
          anyOf:
          - type: string
          - type: 'null'
          title: Email
          description: Email address, stored lowercased. At least one of phone_e164 or email is required.
      additionalProperties: false
      type: object
      required:
      - name
      title: ContactCreate
    MemberPhonePut:
      properties:
        phone_e164:
          type: string
          title: Phone E164
          description: Phone number to save for the caller, E.164 format.
      additionalProperties: false
      type: object
      required:
      - phone_e164
      title: MemberPhonePut
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
          description: Path to the invalid field within the request, as a list of keys/indices (e.g. ['body', 'to']).
        msg:
          type: string
          title: Message
          description: Human-readable description of the validation failure.
        type:
          type: string
          title: Error Type
          description: Machine-readable error type code (e.g. 'missing', 'string_type').
        input:
          title: Input
          description: The value that was actually provided and failed validation.
        ctx:
          type: object
          title: Context
          description: Additional machine-readable context for the error, when the error type provides one.
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
          description: 'List of validation errors: each entry gives the field location (loc), the problem (msg), and the error type (type).'
      type: object
      title: HTTPValidationError
    ContactEntry:
      properties:
        id:
          type: string
          title: Id
          description: '''member:<user_id>'' for an org member, or the contact row''s UUID (as a string) for a manual contact.'
        kind:
          type: string
          enum:
          - member
          - manual
          title: Kind
          description: '''member'' if this row is a member of the organization, ''manual'' if it was added as a contact.'
        name:
          type: string
          title: Name
          description: Display name.
        phone_e164:
          anyOf:
          - type: string
          - type: 'null'
          title: Phone E164
          description: Phone number, E.164 format. Null if none on file.
        email:
          anyOf:
          - type: string
          - type: 'null'
          title: Email
          description: Email address. Null if none on file.
        role:
          anyOf:
          - type: string
          - type: 'null'
          title: Role
          description: Organization role (e.g. 'owner', 'admin', 'member') for kind='member'. Always null for kind='manual'.
      type: object
      required:
      - id
      - kind
      - name
      title: ContactEntry
      description: 'One row in the computed contacts union — an org member or a manual

        contact. ``id`` is ``member:<user_id>`` for members, the contact row''s

        UUID (as str) for manual rows.'
    ContactListResponse:
      properties:
        items:
          items:
            $ref: '#/components/schemas/ContactEntry'
          type: array
          title: Items
          description: Contacts in this page.
        next_cursor:
          anyOf:
          - type: string
          - type: 'null'
          title: Next Cursor
          description: Opaque cursor for the next page. Null when there are no more results.
      type: object
      required:
      - items
      title: ContactListResponse
    ContactPatch:
      properties:
        name:
          anyOf:
          - type: string
            maxLength: 200
            minLength: 1
          - type: 'null'
          title: Name
          description: New display name. Omit to leave unchanged; cannot be set to null.
        phone_e164:
          anyOf:
          - type: string
          - type: 'null'
          title: Phone E164
          description: New phone number, E.164 format. Omit to leave unchanged; explicit null clears it. The contact must keep at least one of phone_e164 or email.
        email:
          anyOf:
          - type: string
          - type: 'null'
          title: Email
          description: New email address, stored lowercased. Omit to leave unchanged; explicit null clears it. The contact must keep at least one of phone_e164 or email.
      additionalProperties: false
      type: object
      title: ContactPatch