Hail Uploads API

The uploads API from Hail — 2 operation(s) for uploads.

GitHub OpenAPI

Operations 2

POST /v1/uploads Create File Upload #
PUT /v1/uploads/{upload_id} Put File Upload #

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-uploads-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-uploads-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Hail Uploads 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: Uploads
paths:
  /v1/uploads:
    post:
      tags:
      - Uploads
      summary: Create File Upload
      description: 'Make a one-time link to upload one file with a plain HTTP PUT.


        For callers whose shell has no Hail key (an agent using the MCP). Send

        the raw bytes to `upload_url` with `PUT` and no Authorization header,

        once, before `expires_at` (15 minutes):


        ```

        curl -sS -X PUT --data-binary @passport.jpg ''''

        ```


        The PUT answers with `size_bytes` and `sha256` of what was stored. An

        organization may hold at most 50 unused, unexpired links.'
      operationId: create_file_upload
      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/FileUploadCreate'
      responses:
        '201':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileUploadLinkResponse'
        '429':
          description: 'Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds). Or: the organization already holds 50 unused, unexpired upload links. Use or let some expire.'
          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
        '503':
          description: File uploads are not configured on this server.
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /v1/uploads/{upload_id}:
    put:
      tags:
      - Uploads
      summary: Put File Upload
      description: 'Send the file''s bytes to an upload link. No Authorization header.


        Works once. A link already expired when the PUT starts is 410. The body

        must arrive before the link expires (and within 10 minutes), or 408.

        Storing it may end after the expiry, up to 15 minutes later. Unknown id,

        wrong token, a used link, and a file that could not be stored at the end

        are all 404 with one message: make a new link if the file is not

        stored. A second upload of the same file is harmless.

        Empty body: 422. A verification document whose first bytes are not the

        declared JPEG, PNG or PDF: 422.'
      operationId: put_file_upload
      parameters:
      - name: upload_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
          title: Upload Id
      - name: token
        in: query
        required: true
        schema:
          type: string
          description: The token from `upload_url`. The only credential.
          title: Token
        description: The token from `upload_url`. The only credential.
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileUploadResponse'
        '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
        '404':
          description: 'Unknown link, wrong token, or already used. Also when the file could not be stored at the end: another PUT used the link first, the link was removed, or storing ended more than 15 minutes after the expiry. If your file is not stored, make a new link and send it again.'
        '408':
          description: The body did not arrive before the link expired, or within 10 minutes. The link is not used up.
        '410':
          description: The link had expired when the PUT started. Make a new link.
        '413':
          description: The file is larger than the link's `max_bytes`.
        '503':
          description: File uploads are not configured on this server, storage failed, or too many uploads are running, in all or for this organization (`Retry-After`). The link is not used up.
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security: []
      requestBody:
        required: true
        description: 'The raw file bytes. The `Content-Type` header is ignored: the type given when the link was made is stored.'
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
components:
  schemas:
    FileUploadLinkResponse:
      properties:
        id:
          type: string
          format: uuid
          title: Id
          description: The upload's id.
        purpose:
          type: string
          enum:
          - email_attachment
          - verification_document
          title: Purpose
          description: What the file is for.
        upload_url:
          type: string
          title: Upload Url
          description: 'Send the raw file bytes here with `PUT`. No Authorization header: the `token` in the URL is the only credential. Works once, until `expires_at`. Treat it like a password.'
        method:
          type: string
          const: PUT
          title: Method
          description: The HTTP method for `upload_url`.
        content_type:
          type: string
          title: Content Type
          description: The type the file is stored with.
        max_bytes:
          type: integer
          title: Max Bytes
          description: The largest file the link takes, in bytes.
        expires_at:
          type: string
          format: date-time
          title: Expires At
          description: When the link stops working.
      type: object
      required:
      - id
      - purpose
      - upload_url
      - method
      - content_type
      - max_bytes
      - expires_at
      title: FileUploadLinkResponse
      description: 'Returned by ``POST /uploads``: a one-time link to send the bytes to.'
    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
    FileUploadResponse:
      properties:
        id:
          type: string
          format: uuid
          title: Id
          description: The upload's id.
        purpose:
          type: string
          enum:
          - email_attachment
          - verification_document
          title: Purpose
          description: What the file is for.
        filename:
          type: string
          title: Filename
          description: The name given when the link was made.
        content_type:
          type: string
          title: Content Type
          description: The type given when the link was made.
        size_bytes:
          type: integer
          title: Size Bytes
          description: Bytes stored. Compare with the local file.
        sha256:
          type: string
          title: Sha256
          description: sha256 of the bytes stored, hex. Compare with the local file.
      type: object
      required:
      - id
      - purpose
      - filename
      - content_type
      - size_bytes
      - sha256
      title: FileUploadResponse
      description: 'Returned by ``PUT /uploads/{id}``: what Hail stored.'
    FileUploadCreate:
      properties:
        purpose:
          type: string
          enum:
          - email_attachment
          - verification_document
          title: Purpose
          description: 'What the file is for. `email_attachment`: the id works in `POST /v1/emails` `attachment_ids`. `verification_document`: the id works as the `upload.<slot>` part of `POST /v1/verifications` and `POST /v1/verifications/check`.'
        filename:
          type: string
          maxLength: 255
          minLength: 1
          title: Filename
          description: The file's name, 1 to 255 chars, no control characters.
        content_type:
          type: string
          title: Content Type
          description: MIME type, `type/subtype` with no parameters. A `verification_document` takes `image/jpeg`, `image/png` or `application/pdf` only. This type is stored; the PUT request's own `Content-Type` header is not used.
      type: object
      required:
      - purpose
      - filename
      - content_type
      title: FileUploadCreate
      description: 'Body of ``POST /uploads``: what the file is for, and its name and type.'