Dropto Deploys API

One publish, from asking for upload addresses to putting a version live. Versions are immutable and are never written over, so promoting an earlier one is a pointer moving rather than an upload repeating.

Operations 7

POST /api/sites/{siteId}/deploys/prepare Validates a manifest and returns one presigned upload URL per file #
GET /api/sites/{siteId}/deploys/{deployId} Reads the state of one deploy #
POST /api/sites/{siteId}/deploys/{deployId}/complete Verifies the uploaded files and takes the deploy live #
GET /api/sites/{siteId}/deploys/{deployId}/file Reads one text file out of one deploy, for the in-browser editor #
GET /api/sites/{siteId}/deploys/{deployId}/manifest Lists the files one deploy contains, a page at a time or all at once #
POST /api/sites/{siteId}/deploys/{deployId}/preview-ticket Lets the in-browser editor's preview frame load a gated version's own files #
POST /api/sites/{siteId}/deploys/{deployId}/promote Rolls the site back to an earlier deploy that still has its files #

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/dropto-run:dropto-run-deploys-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

dropto-run-deploys-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Drop2Run.Api | v1 Deploys API
  version: 1.0.0
  description: One publish, from asking for upload addresses to putting a version live. Versions are immutable and are never written over, so promoting an earlier one is a pointer moving rather than an upload repeating.
servers:
- description: Production
  url: https://dropto.run
tags:
- description: One publish, from asking for upload addresses to putting a version live. Versions are immutable and are never written over, so promoting an earlier one is a pointer moving rather than an upload repeating.
  name: Deploys
paths:
  /api/sites/{siteId}/deploys/prepare:
    post:
      operationId: PrepareDeploy
      parameters:
      - in: path
        name: siteId
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PrepareDeployRequest'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PrepareDeployResponse'
          description: OK
        '400':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Bad Request
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Not Found
        '503':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Service Unavailable
      security:
      - session: []
      - accessToken: []
      - claimToken: []
      - {}
      summary: Validates a manifest and returns one presigned upload URL per file
      tags:
      - Deploys
  /api/sites/{siteId}/deploys/{deployId}:
    get:
      operationId: GetDeploy
      parameters:
      - in: path
        name: siteId
        required: true
        schema:
          type: string
      - in: path
        name: deployId
        required: true
        schema:
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeployDetail'
          description: OK
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Not Found
      security:
      - session: []
      - accessToken: []
      - claimToken: []
      - {}
      summary: Reads the state of one deploy
      tags:
      - Deploys
  /api/sites/{siteId}/deploys/{deployId}/complete:
    post:
      operationId: CompleteDeploy
      parameters:
      - in: path
        name: siteId
        required: true
        schema:
          type: string
      - in: path
        name: deployId
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
              - type: 'null'
              - $ref: '#/components/schemas/CompleteDeployRequest'
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompleteDeployResponse'
          description: OK
        '400':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Bad Request
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Not Found
        '409':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Conflict
      security:
      - session: []
      - accessToken: []
      - claimToken: []
      - {}
      summary: Verifies the uploaded files and takes the deploy live
      tags:
      - Deploys
  /api/sites/{siteId}/deploys/{deployId}/file:
    get:
      operationId: GetDeployFile
      parameters:
      - in: path
        name: siteId
        required: true
        schema:
          type: string
      - in: path
        name: deployId
        required: true
        schema:
          type: string
      - in: query
        name: path
        schema:
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeployFileContent'
          description: OK
        '400':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Bad Request
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Not Found
      security:
      - session: []
      - accessToken: []
      summary: Reads one text file out of one deploy, for the in-browser editor
      tags:
      - Deploys
  /api/sites/{siteId}/deploys/{deployId}/manifest:
    get:
      operationId: GetDeployManifest
      parameters:
      - in: path
        name: siteId
        required: true
        schema:
          type: string
      - in: path
        name: deployId
        required: true
        schema:
          type: string
      - in: query
        name: search
        schema:
          type: string
      - in: query
        name: cursor
        schema:
          type: string
      - in: query
        name: limit
        schema:
          format: int32
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
      - in: query
        name: all
        schema:
          default: false
          type: boolean
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeployManifestPage'
          description: OK
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Not Found
        '409':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Conflict
      security:
      - session: []
      - accessToken: []
      summary: Lists the files one deploy contains, a page at a time or all at once
      tags:
      - Deploys
  /api/sites/{siteId}/deploys/{deployId}/preview-ticket:
    post:
      operationId: IssuePreviewTicket
      parameters:
      - in: path
        name: siteId
        required: true
        schema:
          type: string
      - in: path
        name: deployId
        required: true
        schema:
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreviewTicket'
          description: OK
        '403':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Forbidden
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Not Found
        '503':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Service Unavailable
      security:
      - session: []
      - accessToken: []
      summary: Lets the in-browser editor's preview frame load a gated version's own files
      tags:
      - Deploys
  /api/sites/{siteId}/deploys/{deployId}/promote:
    post:
      operationId: PromoteDeploy
      parameters:
      - in: path
        name: siteId
        required: true
        schema:
          type: string
      - in: path
        name: deployId
        required: true
        schema:
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PromoteDeployResponse'
          description: OK
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Not Found
        '409':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Conflict
        '410':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
          description: Gone
      security:
      - session: []
      - accessToken: []
      - claimToken: []
      - {}
      summary: Rolls the site back to an earlier deploy that still has its files
      tags:
      - Deploys
components:
  schemas:
    PrepareDeployRequest:
      description: Request body of `POST /deploys/prepare`.
      properties:
        batches:
          default: false
          description: 'Whether the client can send batch permits — several small files in one request. A client that does

            not set it receives one permit per file. Setting it is a request, not a guarantee: the server may

            still answer with single permits only.'
          type: boolean
        files:
          description: Every file the client intends to upload.
          items:
            $ref: '#/components/schemas/ManifestEntry'
          minItems: 1
          type: array
      required:
      - files
      type: object
    ProblemDetails:
      properties:
        detail:
          type:
          - 'null'
          - string
        instance:
          type:
          - 'null'
          - string
        status:
          format: int32
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - 'null'
          - integer
          - string
        title:
          type:
          - 'null'
          - string
        type:
          type:
          - 'null'
          - string
      type: object
    DeployManifestPage:
      description: Response of `GET /sites/{siteId}/deploys/{deployId}/manifest`.
      properties:
        deployId:
          description: ULID of the deploy the files belong to.
          type: string
        fileCount:
          description: Files in the whole deploy, whatever the filter matched.
          format: int32
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        files:
          description: This page of matches, ordered by path.
          items:
            $ref: '#/components/schemas/ManifestFile'
          type: array
        filesKept:
          description: 'Whether the deploy is inside the retention window with its objects still on storage, the same answer

            as `filesKept` on the site''s version list. The list is answerable either way — it is read from

            the row — so this is what separates "these files are here" from "these files were here".'
          type: boolean
        matchingBytes:
          description: Their total size, on the same terms.
          format: int64
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        matchingFiles:
          description: Files the filter matched, equal to FileCount without one.
          format: int32
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        nextCursor:
          description: Path to pass back as `cursor` for the following page, or null when this page was the last.
          type:
          - 'null'
          - string
        status:
          description: Lowercase status name of that deploy.
          type: string
        totalBytes:
          description: Size of the whole deploy, whatever the filter matched.
          format: int64
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
      required:
      - deployId
      - status
      - fileCount
      - totalBytes
      - filesKept
      - matchingFiles
      - matchingBytes
      - files
      - nextCursor
      type: object
    PromoteDeployResponse:
      description: Response of `POST /sites/{siteId}/deploys/{deployId}/promote`.
      properties:
        deployId:
          description: ULID of the deploy that is now live.
          type: string
        edge:
          description: '`live` when the edge has the deploy, `pending` when the rollback is committed but the edge

            write did not land yet; it gets there on its own, as on `complete`.'
          type: string
        fileCount:
          description: Files in the promoted deploy.
          format: int32
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        status:
          description: Always `live` on success.
          type: string
        totalBytes:
          description: Its total size in bytes.
          format: int64
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        url:
          description: Public URL the site answers on. Content may take up to a minute to reach every edge.
          type: string
      required:
      - deployId
      - status
      - url
      - fileCount
      - totalBytes
      - edge
      type: object
    DeployFileContent:
      description: Response of `GET /sites/{siteId}/deploys/{deployId}/file`.
      properties:
        path:
          description: The path, normalized — which is the spelling the manifest holds.
          type: string
        sha256:
          description: 'Digest of the content as stored.


            Sent so the editor can publish a change without re-reading anything: the new manifest is the old one

            with this entry''s digest replaced, and every other file is reused by `prepare`.'
          type: string
        size:
          description: Size in bytes, as the manifest records it.
          format: int64
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        text:
          description: 'The file''s text, or null when the bytes are not valid UTF-8.


            Null rather than a lossy decode, so the dashboard can say the file cannot be edited here instead of

            offering an editor whose contents would overwrite the original with replacement characters.'
          type:
          - 'null'
          - string
      required:
      - path
      - sha256
      - size
      - text
      type: object
    CompleteDeployRequest:
      description: Request body of `POST /deploys/{deployId}/complete`.
      properties:
        name:
          description: 'A display name for the site, read by the client from the `<title>` of the

            `index.html` being published.


            Applied only to a site that has no name yet, and only on the call that takes a deploy live — so a

            redeploy never renames a site, and a retried completion never renames one either.


            Cleaned by SiteNames before it is stored, and dropped silently when nothing

            usable is left. It is not worth failing a deploy whose files have already been verified over the

            contents of a title tag, and the owner can type a name on the settings page regardless.'
          type:
          - 'null'
          - string
      type: object
    PrepareDeployResponse:
      description: Response of `POST /deploys/prepare`.
      properties:
        batchConcurrency:
          description: 'How many of Batches to send at once; null when there are none. The upload Worker can

            hold only six R2 writes open per request, so this is what decides how fast many small files go, and it

            is set here so it can be tuned without a client release. Single permits are not affected.'
          format: int32
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - 'null'
          - integer
          - string
        batches:
          description: 'Batch permits, each covering several small files sent as one body. Empty unless the request asked

            for batches and they are enabled; a file is in exactly one of Upload and this.'
          items:
            $ref: '#/components/schemas/UploadBatch'
          type: array
        deployId:
          description: ULID of the new deploy; part of every R2 key it writes.
          type: string
        reused:
          description: 'How many files the live deploy already holds byte-identical. These are absent from

            Upload and are copied server-side when the deploy completes.'
          format: int32
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        total:
          description: 'Files in the whole deploy: Reused plus what must be uploaded.'
          format: int32
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        unchanged:
          default: false
          description: 'True when the manifest describes exactly what is already live, so no deploy was created. The caller

            has nothing to upload and must not call `complete`: DeployId is the deploy

            already serving, and completing it again would only rewrite the mapping to where it already points.'
          type: boolean
        upload:
          description: One upload permit per file sent on its own.
          items:
            $ref: '#/components/schemas/UploadTarget'
          type: array
        uploadUrl:
          description: 'The single endpoint every permit is presented to. Sent with the response rather than compiled into

            the client, because it is a different host in development, in tests and in production — and because

            a client that knew the address without being told would still know it after we moved.'
          type: string
        url:
          description: 'Public URL of the site, sent only when Unchanged is true. That answer is the end

            of the deploy, and without it the caller would have to ask a second endpoint just to show the link.'
          type:
          - 'null'
          - string
        warnings:
          description: 'Things about this deploy the caller should be told although it was accepted, or null when there are none —

            today only that `_functions/` will not run on this site (`docs/briefs/FUNCTIONS-BRIEF.md` D23).'
          items:
            type: string
          type:
          - 'null'
          - array
      required:
      - deployId
      - upload
      - batches
      - uploadUrl
      - reused
      - total
      type: object
    UploadBatch:
      description: Several small files the client sends as one body, under one permit.
      properties:
        files:
          description: The files, in the order their bytes must follow the layout.
          items:
            $ref: '#/components/schemas/BatchFile'
          type: array
        token:
          description: Bearer token authorising exactly these files.
          type: string
      required:
      - token
      - files
      type: object
    DeployDetail:
      description: Response of `GET /sites/{siteId}/deploys/{deployId}`.
      properties:
        completedAt:
          description: When it reached a terminal state, or null while in flight.
          format: date-time
          type:
          - 'null'
          - string
        createdAt:
          description: When it was prepared.
          format: date-time
          type: string
        deployId:
          description: ULID of the deploy.
          type: string
        error:
          description: Why it failed, when it did.
          type:
          - 'null'
          - string
        fileCount:
          description: Files it holds.
          format: int32
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        isLive:
          description: Whether this is the deploy currently being served.
          type: boolean
        siteId:
          description: ULID of its site.
          type: string
        status:
          description: Lowercase status name.
          type: string
        totalBytes:
          description: Its size.
          format: int64
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        url:
          description: The site's URL, present only when this deploy is live.
          type:
          - 'null'
          - string
      required:
      - deployId
      - siteId
      - status
      - isLive
      - fileCount
      - totalBytes
      - createdAt
      - completedAt
      - error
      - url
      type: object
    ManifestEntry:
      description: One entry in the client's manifest. Entirely untrusted until validated.
      properties:
        path:
          description: Path relative to the deploy root, as the client sees it.
          type:
          - 'null'
          - string
        sha256:
          description: SHA-256 of the content, lowercase hex.
          type:
          - 'null'
          - string
        size:
          description: 'Exact length of the content in bytes — the length that will be uploaded. 0 only for an empty file,

            whose checksum is the SHA-256 of zero bytes.'
          format: int64
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
      required:
      - path
      - sha256
      - size
      type: object
    PreviewTicket:
      description: Response of `POST /sites/{siteId}/deploys/{deployId}/preview-ticket`.
      properties:
        action:
          description: The URL to post the ticket to, as the form field `ticket`, targeting the preview frame.
          type: string
        expiresAt:
          description: When the frame's access runs out and a new ticket is needed.
          format: date-time
          type: string
        ticket:
          description: The signed ticket. Accepted for one minute.
          type: string
      required:
      - action
      - ticket
      - expiresAt
      type: object
    ManifestFile:
      description: One file in a deploy's verified manifest.
      properties:
        path:
          description: Its path within the site, already normalized and always /-separated.
          type: string
        sha256:
          description: SHA-256 of the content, lowercase hex — what the upload was verified against.
          type: string
        size:
          description: Its size in bytes.
          format: int64
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
      required:
      - path
      - sha256
      - size
      type: object
    CompleteDeployResponse:
      description: Response of `POST /deploys/{deployId}/complete`.
      properties:
        deployId:
          description: ULID of the deploy that is now live.
          type: string
        docsMode:
          description: 'Whether the site is read through the documents viewer rather than served as files.


            Reported for the same reason as Name: the server decides it, from the files this

            deploy actually holds (`SiteModeDetection`), and a client that guessed from what it uploaded

            would be wrong exactly when it matters — the web app that went live inside the reader because its

            `index.html` sat one folder too deep, with nothing on the finished card saying so.'
          type: boolean
        edge:
          description: '`live` when the edge has the deploy, `pending` when the deploy is live but the edge write did

            not land yet — Cloudflare refused it or its API budget is spent. A pending deploy is not a failure: it

            reaches the edge on its own, usually within the `Retry-After` the response carries, and calling

            `complete` again is safe but not needed.'
          type: string
        fileCount:
          description: Number of verified files.
          format: int32
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        modeIsManual:
          description: Whether the owner chose the mode by hand, in which case no deploy changes it.
          type: boolean
        name:
          description: 'What the site is called now, or null when it has none and its subdomain should be shown instead.


            Reported rather than left for the client to infer, because only the server knows the answer: it

            cleans the name it is sent, and it refuses to overwrite a name the site already had. A client that

            assumed the title it sent is the name now stored would be wrong on every redeploy.


            What the anonymous flow needs. A site is remembered in the browser before its first deploy starts —

            the claim token is returned once and storing it cannot wait — so the name is not known at that point

            and arrives here instead.'
          type:
          - 'null'
          - string
        spaMode:
          description: Whether unmatched paths are answered with the site's `index.html`.
          type: boolean
        status:
          description: Always `live` on success.
          type: string
        totalBytes:
          description: Total verified size in bytes.
          format: int64
          pattern: ^-?(?:0|[1-9]\d*)$
          type:
          - integer
          - string
        url:
          description: Public URL the site answers on. Content may take up to a minute to reach every edge.
          type: string
      required:
      - deployId
      - status
      - url
      - fileCount
      - totalBytes
      - name
      - spaMode
      - docsMode
      - modeIsManual
      - edge
      type: object
    UploadTarget:
      description: Where the client should PUT one file, and what the PUT must carry.
      properties:
        path:
          description: Normalized path, which is also what the manifest records.
          type: string
        sha256:
          description: 'Digest signed into Token, echoed back because the client needs it to name the

            file it is sending. It is the same value the client put in the manifest; returning it saves the

            client from keeping its own manifest indexed by path just to find it again.'
          type: string
        token:
          description: 'Bearer token authorising exactly this object. Signed by the control plane and verified by the upload

            Worker, which refuses a second write of the same key without touching R2.'
          type: string
      required:
      - path
      - token
      - sha256
      type: object
    BatchFile:
      description: One file inside a batch.
      properties:
        path:
          description: Normalized path, which the client uses to find the bytes and to name a failure.
          type: string
        sha256:
          description: Digest, which is also how the layout line and the object's key are built.
          type: string
      required:
      - path
      - sha256
      type: object
  securitySchemes:
    accessToken:
      description: 'A personal access token, created at /account/tokens. Sent as `Authorization: Bearer d2r_...`. A rejected token answers 401 with `invalid_token`, which is deliberately distinct from the 403 a valid credential gets for something it may not do.'
      scheme: bearer
      type: http
    claimToken:
      description: Returned once when an anonymous site is created, and the only credential that site has until somebody signs in and claims it.
      in: header
      name: X-Claim-Token
      type: apiKey
    session:
      description: The browser session, set by signing in with Google. HttpOnly, so a script cannot read it and a client outside a browser cannot send it.
      in: cookie
      name: d2r_session
      type: apiKey