{
  "openapi": "3.0.0",
  "info": {
    "title": "Slingshot",
    "description": "_A [gravitational slingshot](https://en.wikipedia.org/wiki/Gravity_assist) makes use of the gravity and relative movements of celestial bodies to accelerate a spacecraft and change its trajectory._\n\n\n# Slingshot: edge record and identity cache\n\nApplications in [ATProtocol](https://atproto.com/) store data in users' own [PDS](https://atproto.com/guides/self-hosting) (Personal Data Server), which are distributed across thousands of independently-run servers all over the world. Trying to access this data poses challenges for client applications:\n\n- A PDS might be far away with long network latency\n- or may be on an unreliable connection\n- or overloaded when you need it, or offline, or…\n\nLarge projects like [Bluesky](https://bsky.app/) control their performance and reliability by syncing all app-relevant data from PDSs into first-party databases. But for new apps, building out this additional data infrastructure adds significant effort and complexity up front.\n\n**Slingshot is a fast, eager, production-grade cache of data in the [ATmosphere](https://atproto.com/)**, offering performance and reliability without custom infrastructure.\n\n\n### Current status\n\n> [!important]\n> Slingshot is currently in a **v0, pre-release state**. There is one production instance and you can use it! Expect short downtimes for restarts as development progresses and lower cache hit-rates as the internal storage caches are adjusted and reset.\n\nThe core APIs will not change, since they are standard third-party `com.atproto` query APIs from ATProtocol.\n\n\n## Eager caching\n\nIn many cases, Slingshot can cache the data you need *before* first request!\n\nSlingshot subscribes to the global [Firehose](https://atproto.com/specs/sync#firehose) of data updates. It keeps a short-term rolling indexed window of *all* data, and automatically promotes content likely to be requested to its longer-term main cache. _(automatic promotion is still a work in progress)_\n\nWhen there is a cache miss, Slingshot can often still accelerate record fetching, since it keeps a large cache of resolved identities: it can usually request from the correct PDS without extra lookups.\n\n\n## Precise invalidation\n\nThe fireshose includes **update** and **delete** events, which Slingshot uses to ensure stale and deleted data is removed within a very short window. Additonally, identity and account-level events can trigger rapid cleanup of data for deactivated and deleted accounts. _(some of this is still a work in progress)_\n\n\n## Low-trust\n\nThe \"AT\" in ATProtocol [stands for _Authenticated Transfer_](https://atproto.com/guides/glossary#at-protocol): all data is cryptographically signed, which makes it possible to broadcast data through third parties and trust that it's real _without_ having to directly contact the originating server.\n\nTwo core standard query APIs are supported to balance convenience and trust. They both fetch [records](https://atproto.com/guides/glossary#record):\n\n### [`com.atproto.repo.getRecord`](#tag/comatproto-queries/get/xrpc/com.atproto.repo.getRecord)\n\n- convenient `JSON` response format\n- cannot be proven authentic\n\n### [`com.atproto.sync.getRecord`](#tag/comatproto-queries/get/xrpc/com.atproto.sync.getRecord)\n\n- [`DAG-CBOR`](https://atproto.com/specs/data-model)-encoded response requires extra libraries to decode, but\n- includes a cryptographic proof of authenticity!\n\n_(work on this endpoint is in progress)_\n\n\n## Service proxying\n\nClients can proxy atproto queries through their own PDS with [Service Proxying](https://atproto.com/specs/xrpc#service-proxying), and this is supported by Slingshot. The Slingshot instance must be started the `--domain` argument specified.\n\nService-proxied requests can specify a Slingshot instance via the `atproto-proxy` header:\n\n```http\nGET /xrpc/com.bad-example.identity.resolveMiniDoc?identifier=bad-example.com\nHost: <your pds>\natproto-proxy: did:web:<slingshot domain>#slingshot\n```\n\nWhere `<your pds>` is the user's own PDS host, and `<slingshot domain>` is the domain that the slingshot instance is deployed at (eg. `slingshot.microcosm.blue`). See the [Service Proxying](https://atproto.com/specs/xrpc#service-proxying) docs for more.\n\n> [!tip]\n> Service proxying is supported but completely optional. All APIs are directly accessible over the public internet, and GeoDNS helps route users to the closest instance to them for the lowest possible latency. (_note: deploying multiple slingshot instances with GeoDNS is still TODO_)\n\n\n## Ergonomic APIs\n\n- Slingshot also offers variants of the `getRecord` endpoints that accept a full `at-uri` as a parameter, to save clients from needing to parse and validate all parts of a record location.\n\n- Bi-directionally verifying identity endpoints, so you can directly exchange atproto [`handle`](https://atproto.com/guides/glossary#handle)s for [`DID`](https://atproto.com/guides/glossary#did-decentralized-id)s without extra steps, plus a convenient [Mini-Doc](#tag/slingshot-specific-queries/get/xrpc/com.bad-example.identity.resolveMiniDoc) verified identity summary.\n\n\n## Part of microcosm\n\n[Microcosm](https://www.microcosm.blue/) is a collection of services and independent community-run infrastructure for ATProtocol.\n\nSlingshot excels when combined with _shallow indexing_ services, which offer fast queries of global data relationships but with only references to the data records. Microcosm has a few!\n\n- [🌌 Constellation](https://constellation.microcosm.blue/), a global backlink index (all social interactions in atproto are links!)\n- [🎇 Spacedust](https://spacedust.microcosm.blue/), a firehose of all social interactions\n\n> [!success]\n> All microcosm projects are [open source](https://tangled.org/bad-example.com/microcosm-links). **You can help sustain Slingshot** and all of microcosm by becoming a [Github sponsor](https://github.com/sponsors/uniphil/) or a [Ko-fi supporter](https://ko-fi.com/bad_example)!\n",
    "version": "0.1.0",
    "contact": {
      "name": "@microcosm.blue",
      "url": "https://bsky.app/profile/microcosm.blue"
    }
  },
  "servers": [
    {
      "url": "https://slingshot.microcosm.blue"
    }
  ],
  "tags": [
    {
      "name": "com.atproto.* queries",
      "description": "Core ATProtocol-compatible APIs.\n\n> [!tip]\n> Upstream documentation is available at\n> https://docs.bsky.app/docs/category/http-reference\n\nThese queries are usually executed directly against the PDS containing\nthe data being requested. Slingshot offers a caching view of the same\ncontents with better expected performance and reliability."
    },
    {
      "name": "slingshot-specific queries",
      "description": "Additional and improved APIs.\n\nThese APIs offer small tweaks to the core ATProtocol APIs, with more\nmore convenient [request parameters](#tag/slingshot-specific-queries/GET/xrpc/com.bad-example.repo.getUriRecord)\nor [response formats](#tag/slingshot-specific-queries/GET/xrpc/com.bad-example.identity.resolveMiniDoc).\n\n> [!important]\n> At the moment, these are namespaced under the `com.bad-example.*` NSID\n> prefix, but as they stabilize they may be migrated to an org namespace\n> like `blue.microcosm.*`. Support for asliasing to `com.bad-example.*`\n> will be maintained as long as it's in use."
    }
  ],
  "paths": {
    "/xrpc/com.atproto.repo.getRecord": {
      "get": {
        "tags": [
          "com.atproto.* queries"
        ],
        "summary": "com.atproto.repo.getRecord",
        "description": "Get a single record from a repository. Does not require auth.\n\n> [!tip]\n> See also the [canonical `com.atproto` XRPC documentation](https://docs.bsky.app/docs/api/com-atproto-repo-get-record)\n> that this endpoint aims to be compatible with.",
        "parameters": [
          {
            "name": "repo",
            "schema": {
              "type": "string",
              "example": "did:plc:hdhoaan3xa3jiuq4fg4mefid"
            },
            "in": "query",
            "description": "The DID or handle of the repo",
            "required": true,
            "deprecated": false,
            "explode": true
          },
          {
            "name": "collection",
            "schema": {
              "type": "string",
              "example": "app.bsky.feed.like"
            },
            "in": "query",
            "description": "The NSID of the record collection",
            "required": true,
            "deprecated": false,
            "explode": true
          },
          {
            "name": "rkey",
            "schema": {
              "type": "string",
              "example": "3lv4ouczo2b2a"
            },
            "in": "query",
            "description": "The Record key",
            "required": true,
            "deprecated": false,
            "explode": true
          },
          {
            "name": "cid",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "description": "Optional: the CID of the version of the record.\n\nIf not specified, then return the most recent version.\n\nIf a stale `CID` is specified and a newer version of the record\nexists, Slingshot returns a `NotFound` error. That is: Slingshot\nonly retains the most recent version of a record.",
            "required": false,
            "deprecated": false,
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Record found",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/FoundRecordResponseObject"
                }
              }
            }
          },
          "400": {
            "description": "Bad request or no record to return\n\nThe only error name in the repo.getRecord lexicon is `RecordNotFound`,\nbut the [canonical api docs](https://docs.bsky.app/docs/api/com-atproto-repo-get-record)\nalso list `InvalidRequest`, `ExpiredToken`, and `InvalidToken`. Of\nthese, slingshot will only generate `RecordNotFound` or `InvalidRequest`,\nbut may return any proxied error code from the upstream repo.",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          },
          "500": {
            "description": "Server errors",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          }
        }
      }
    },
    "/xrpc/blue.microcosm.repo.getRecordByUri": {
      "get": {
        "tags": [
          "slingshot-specific queries"
        ],
        "summary": "blue.microcosm.repo.getRecordByUri",
        "description": "alias of `com.bad-example.repo.getUriRecord` with intention to stabilize under this name",
        "parameters": [
          {
            "name": "at_uri",
            "schema": {
              "type": "string",
              "example": "at://did:plc:hdhoaan3xa3jiuq4fg4mefid/app.bsky.feed.like/3lv4ouczo2b2a"
            },
            "in": "query",
            "description": "The at-uri of the record\n\nThe identifier can be a DID or an atproto handle, and the collection\nand rkey segments must be present.",
            "required": true,
            "deprecated": false,
            "explode": true
          },
          {
            "name": "cid",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "description": "Optional: the CID of the version of the record.\n\nIf not specified, then return the most recent version.\n\n> [!tip]\n> If specified and a newer version of the record exists, returns 404 not\n> found. That is: slingshot only retains the most recent version of a\n> record.",
            "required": false,
            "deprecated": false,
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Record found",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/FoundRecordResponseObject"
                }
              }
            }
          },
          "400": {
            "description": "Bad request or no record to return\n\nThe only error name in the repo.getRecord lexicon is `RecordNotFound`,\nbut the [canonical api docs](https://docs.bsky.app/docs/api/com-atproto-repo-get-record)\nalso list `InvalidRequest`, `ExpiredToken`, and `InvalidToken`. Of\nthese, slingshot will only generate `RecordNotFound` or `InvalidRequest`,\nbut may return any proxied error code from the upstream repo.",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          },
          "500": {
            "description": "Server errors",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          }
        }
      }
    },
    "/xrpc/com.bad-example.repo.getUriRecord": {
      "get": {
        "tags": [
          "slingshot-specific queries"
        ],
        "summary": "com.bad-example.repo.getUriRecord",
        "description": "Ergonomic complement to [`com.atproto.repo.getRecord`](https://docs.bsky.app/docs/api/com-atproto-repo-get-record)\nwhich accepts an `at-uri` instead of individual repo/collection/rkey params",
        "parameters": [
          {
            "name": "at_uri",
            "schema": {
              "type": "string",
              "example": "at://did:plc:hdhoaan3xa3jiuq4fg4mefid/app.bsky.feed.like/3lv4ouczo2b2a"
            },
            "in": "query",
            "description": "The at-uri of the record\n\nThe identifier can be a DID or an atproto handle, and the collection\nand rkey segments must be present.",
            "required": true,
            "deprecated": false,
            "explode": true
          },
          {
            "name": "cid",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "description": "Optional: the CID of the version of the record.\n\nIf not specified, then return the most recent version.\n\n> [!tip]\n> If specified and a newer version of the record exists, returns 404 not\n> found. That is: slingshot only retains the most recent version of a\n> record.",
            "required": false,
            "deprecated": false,
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Record found",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/FoundRecordResponseObject"
                }
              }
            }
          },
          "400": {
            "description": "Bad request or no record to return\n\nThe only error name in the repo.getRecord lexicon is `RecordNotFound`,\nbut the [canonical api docs](https://docs.bsky.app/docs/api/com-atproto-repo-get-record)\nalso list `InvalidRequest`, `ExpiredToken`, and `InvalidToken`. Of\nthese, slingshot will only generate `RecordNotFound` or `InvalidRequest`,\nbut may return any proxied error code from the upstream repo.",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          },
          "500": {
            "description": "Server errors",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          }
        }
      }
    },
    "/xrpc/com.atproto.identity.resolveHandle": {
      "get": {
        "tags": [
          "com.atproto.* queries"
        ],
        "summary": "com.atproto.identity.resolveHandle",
        "description": "Resolves an atproto [`handle`](https://atproto.com/guides/glossary#handle)\n(hostname) to a [`DID`](https://atproto.com/guides/glossary#did-decentralized-id).\n\n> [!tip]\n> Compatibility note: Slingshot will **always bi-directionally verify\n> against the DID document**, which is optional according to the\n> authoritative lexicon.\n\n> [!tip]\n> See the [canonical `com.atproto` XRPC documentation](https://docs.bsky.app/docs/api/com-atproto-identity-resolve-handle)\n> that this endpoint aims to be compatible with.",
        "parameters": [
          {
            "name": "handle",
            "schema": {
              "type": "string",
              "example": "bad-example.com"
            },
            "in": "query",
            "description": "The handle to resolve.",
            "required": true,
            "deprecated": false,
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Resolution succeeded",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/FoundDidResponseObject"
                }
              }
            }
          },
          "400": {
            "description": "Bad request, failed to resolve, or failed to verify\n\n`error` will be one of `InvalidRequest`, `HandleNotFound`.",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong trying to complete the request",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          }
        }
      }
    },
    "/xrpc/blue.microcosm.identity.resolveMiniDoc": {
      "get": {
        "tags": [
          "slingshot-specific queries"
        ],
        "summary": "blue.microcosm.identity.resolveMiniDoc",
        "description": "alias of `com.bad-example.identity.resolveMiniDoc` with intention to stabilize under this name",
        "parameters": [
          {
            "name": "identifier",
            "schema": {
              "type": "string",
              "example": "bad-example.com"
            },
            "in": "query",
            "description": "Handle or DID to resolve",
            "required": true,
            "deprecated": false,
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Identity resolved",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/MiniDocResponseObject"
                }
              }
            }
          },
          "400": {
            "description": "Bad request or identity not resolved",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          }
        }
      }
    },
    "/xrpc/com.bad-example.identity.resolveMiniDoc": {
      "get": {
        "tags": [
          "slingshot-specific queries"
        ],
        "summary": "com.bad-example.identity.resolveMiniDoc",
        "description": "Like [com.atproto.identity.resolveIdentity](https://docs.bsky.app/docs/api/com-atproto-identity-resolve-identity)\nbut instead of the full `didDoc` it returns an atproto-relevant subset.",
        "parameters": [
          {
            "name": "identifier",
            "schema": {
              "type": "string",
              "example": "bad-example.com"
            },
            "in": "query",
            "description": "Handle or DID to resolve",
            "required": true,
            "deprecated": false,
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Identity resolved",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/MiniDocResponseObject"
                }
              }
            }
          },
          "400": {
            "description": "Bad request or identity not resolved",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          }
        }
      }
    },
    "/xrpc/com.bad-example.identity.resolveService": {
      "get": {
        "tags": [
          "slingshot-specific queries"
        ],
        "summary": "com.bad-example.identity.resolveService",
        "description": "resolve an atproto service did + id to its http endpoint\n\n> [!important]\n> this endpoint is experimental and may change",
        "parameters": [
          {
            "name": "did",
            "schema": {
              "type": "string",
              "example": "did:web:constellation.microcosm.blue"
            },
            "in": "query",
            "description": "the service's did",
            "required": true,
            "deprecated": false,
            "explode": true
          },
          {
            "name": "id",
            "schema": {
              "type": "string",
              "example": "#constellation"
            },
            "in": "query",
            "description": "id fragment, starting with '#'\n\nmust be url-encoded!",
            "required": true,
            "deprecated": false,
            "explode": true
          },
          {
            "name": "type",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "description": "optionally, the exact service type to filter\n\nresolving a pds requires matching the type as well as id. service\nproxying ignores the type.",
            "required": false,
            "deprecated": false,
            "explode": true
          }
        ],
        "responses": {
          "200": {
            "description": "Service resolved",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceResponseObject"
                }
              }
            }
          },
          "400": {
            "description": "Bad request or service not resolved",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          }
        }
      }
    },
    "/xrpc/com.bad-example.proxy.hydrateQueryResponse": {
      "post": {
        "tags": [
          "slingshot-specific queries"
        ],
        "summary": "com.bad-example.proxy.hydrateQueryResponse",
        "description": "> [!important]\n> Unstable! This endpoint is experimental and may change.\n\nFetch + include records referenced from an upstream xrpc query response",
        "requestBody": {
          "content": {
            "application/json; charset=utf-8": {
              "schema": {
                "$ref": "#/components/schemas/ProxyQueryPayload"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/ProxyHydrateResponseObject"
                }
              }
            }
          },
          "400": {
            "description": "",
            "content": {
              "application/json; charset=utf-8": {
                "schema": {
                  "$ref": "#/components/schemas/XrpcErrorResponseObject"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "FoundDidResponseObject": {
        "type": "object",
        "title": "FoundDidResponseObject",
        "required": [
          "did"
        ],
        "properties": {
          "did": {
            "type": "string",
            "description": "the DID, bi-directionally verified if using Slingshot"
          }
        },
        "example": {
          "did": "did:plc:hdhoaan3xa3jiuq4fg4mefid"
        }
      },
      "FoundRecordResponseObject": {
        "type": "object",
        "title": "FoundRecordResponseObject",
        "required": [
          "uri",
          "value"
        ],
        "properties": {
          "uri": {
            "type": "string",
            "description": "at-uri for this record"
          },
          "cid": {
            "type": "string",
            "description": "CID for this exact version of the record\n\nSlingshot will always return the CID, despite it not being a required\nresponse property in the official lexicon.\n\nTODO: probably actually let it be optional, idk are some pds's weirdly\nnot returning it?"
          },
          "value": {
            "description": "the record itself as JSON"
          }
        },
        "example": {
          "cid": "bafyreialv3mzvvxaoyrfrwoer3xmabbmdchvrbyhayd7bga47qjbycy74e",
          "uri": "at://did:plc:hdhoaan3xa3jiuq4fg4mefid/app.bsky.feed.like/3lv4ouczo2b2a",
          "value": {
            "$type": "app.bsky.feed.like",
            "createdAt": "2025-07-29T18:02:02.327Z",
            "subject": {
              "cid": "bafyreia2gy6eyk5qfetgahvshpq35vtbwy6negpy3gnuulcdi723mi7vxy",
              "uri": "at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.feed.post/3lv4lkb4vgs2k"
            }
          }
        }
      },
      "HydrationSource": {
        "type": "object",
        "title": "HydrationSource",
        "required": [
          "path",
          "shape"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "Record Path syntax for locating fields"
          },
          "shape": {
            "type": "string",
            "description": "What to expect at the path: 'strong-ref', 'at-uri', 'at-uri-parts', 'did', 'handle', or 'at-identifier'.\n\n- `strong-ref`: object in the shape of `com.atproto.repo.strongRef` with `uri` and `cid` keys.\n- `at-uri`: string, must have all segments present (identifier, collection, rkey)\n- `at-uri-parts`: object with keys (`repo` or `did`), `collection`, `rkey`, and optional `cid`. Other keys may be present and will be ignored.\n- `did`: string, `did` format\n- `handle`: string, `handle` format\n- `at-identifier`: string, `did` or `handle` format"
          }
        }
      },
      "Hydration_FoundRecordResponseObject": {
        "type": "object",
        "anyOf": [
          {
            "$ref": "#/components/schemas/Hydration_FoundRecordResponseObject_Error"
          },
          {
            "$ref": "#/components/schemas/Hydration_FoundRecordResponseObject_Pending"
          },
          {
            "$ref": "#/components/schemas/Hydration_FoundRecordResponseObject_Found"
          }
        ],
        "discriminator": {
          "propertyName": "status",
          "mapping": {
            "error": "#/components/schemas/Hydration_FoundRecordResponseObject_Error",
            "pending": "#/components/schemas/Hydration_FoundRecordResponseObject_Pending",
            "found": "#/components/schemas/Hydration_FoundRecordResponseObject_Found"
          }
        }
      },
      "Hydration_FoundRecordResponseObject_Error": {
        "allOf": [
          {
            "type": "object",
            "required": [
              "status"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "error"
                ],
                "example": "error"
              }
            }
          },
          {
            "$ref": "#/components/schemas/ProxyHydrationError"
          }
        ]
      },
      "Hydration_FoundRecordResponseObject_Found": {
        "allOf": [
          {
            "type": "object",
            "required": [
              "status"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "found"
                ],
                "example": "found"
              }
            }
          },
          {
            "$ref": "#/components/schemas/FoundRecordResponseObject"
          }
        ]
      },
      "Hydration_FoundRecordResponseObject_Pending": {
        "allOf": [
          {
            "type": "object",
            "required": [
              "status"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "pending"
                ],
                "example": "pending"
              }
            }
          },
          {
            "$ref": "#/components/schemas/ProxyHydrationPending"
          }
        ]
      },
      "Hydration_MiniDocResponseObject": {
        "type": "object",
        "anyOf": [
          {
            "$ref": "#/components/schemas/Hydration_MiniDocResponseObject_Error"
          },
          {
            "$ref": "#/components/schemas/Hydration_MiniDocResponseObject_Pending"
          },
          {
            "$ref": "#/components/schemas/Hydration_MiniDocResponseObject_Found"
          }
        ],
        "discriminator": {
          "propertyName": "status",
          "mapping": {
            "error": "#/components/schemas/Hydration_MiniDocResponseObject_Error",
            "pending": "#/components/schemas/Hydration_MiniDocResponseObject_Pending",
            "found": "#/components/schemas/Hydration_MiniDocResponseObject_Found"
          }
        }
      },
      "Hydration_MiniDocResponseObject_Error": {
        "allOf": [
          {
            "type": "object",
            "required": [
              "status"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "error"
                ],
                "example": "error"
              }
            }
          },
          {
            "$ref": "#/components/schemas/ProxyHydrationError"
          }
        ]
      },
      "Hydration_MiniDocResponseObject_Found": {
        "allOf": [
          {
            "type": "object",
            "required": [
              "status"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "found"
                ],
                "example": "found"
              }
            }
          },
          {
            "$ref": "#/components/schemas/MiniDocResponseObject"
          }
        ]
      },
      "Hydration_MiniDocResponseObject_Pending": {
        "allOf": [
          {
            "type": "object",
            "required": [
              "status"
            ],
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "pending"
                ],
                "example": "pending"
              }
            }
          },
          {
            "$ref": "#/components/schemas/ProxyHydrationPending"
          }
        ]
      },
      "MiniDocResponseObject": {
        "type": "object",
        "title": "MiniDocResponseObject",
        "required": [
          "did",
          "handle",
          "pds",
          "signing_key"
        ],
        "properties": {
          "did": {
            "type": "string",
            "description": "DID, bi-directionally verified if a handle was provided in the query."
          },
          "handle": {
            "type": "string",
            "description": "The validated handle of the account or `handle.invalid` if the handle\ndid not bi-directionally match the DID document."
          },
          "pds": {
            "type": "string",
            "description": "The identity's PDS URL"
          },
          "signing_key": {
            "type": "string",
            "description": "The atproto signing key publicKeyMultibase\n\nLegacy key encoding not supported. the key is returned directly; `id`,\n`type`, and `controller` are omitted."
          }
        },
        "example": {
          "did": "did:plc:hdhoaan3xa3jiuq4fg4mefid",
          "handle": "bad-example.com",
          "pds": "https://porcini.us-east.host.bsky.network",
          "signing_key": "zQ3shpq1g134o7HGDb86CtQFxnHqzx5pZWknrVX2Waum3fF6j"
        }
      },
      "ProxyHydrateResponseObject": {
        "type": "object",
        "title": "ProxyHydrateResponseObject",
        "required": [
          "output",
          "records",
          "identifiers"
        ],
        "properties": {
          "output": {
            "description": "The original upstream response content"
          },
          "records": {
            "type": "object",
            "description": "Any hydrated records",
            "additionalProperties": {
              "$ref": "#/components/schemas/Hydration_FoundRecordResponseObject"
            }
          },
          "identifiers": {
            "type": "object",
            "description": "Any hydrated identifiers",
            "additionalProperties": {
              "$ref": "#/components/schemas/Hydration_MiniDocResponseObject"
            }
          }
        },
        "example": {
          "identifiers": {},
          "output": {},
          "records": {
            "asdf": {
              "followUp": "/xrpc/com.atproto.repo.getRecord?...",
              "reason": "deadline",
              "status": "pending"
            }
          }
        }
      },
      "ProxyHydrationError": {
        "type": "object",
        "title": "ProxyHydrationError",
        "required": [
          "reason",
          "shouldRetry",
          "followUp"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "description": "Short description of why the hydration failed"
          },
          "shouldRetry": {
            "type": "boolean",
            "description": "Whether or not it's recommended to retry requesting this item"
          },
          "followUp": {
            "type": "string",
            "description": "URL to follow up at if retrying"
          }
        }
      },
      "ProxyHydrationPending": {
        "type": "object",
        "title": "ProxyHydrationPending",
        "required": [
          "followUp",
          "reason"
        ],
        "properties": {
          "followUp": {
            "type": "string",
            "description": "URL you can request to finish hydrating this item"
          },
          "reason": {
            "type": "string",
            "description": "Why this item couldn't be hydrated: 'deadline' or 'limit'\n\n- `deadline`: the item fetch didn't complete before the response was\ndue, but will continue on slingshot in the background -- `followUp`\nrequests are coalesced into the original item fetch to be available as\nearly as possible.\n\n- `limit`: slingshot only attempts to hydrate the first 100 items found\nin a proxied response, with the remaining marked `pending`. You can\nrequest `followUp` to fetch them.\n\nIn the future, Slingshot may put pending links after `limit` into a low-\npriority fetch queue, so that these items become available sooner on\nfollow-up request as well."
          }
        }
      },
      "ProxyQueryPayload": {
        "type": "object",
        "title": "ProxyQueryPayload",
        "required": [
          "xrpc",
          "atproto_proxy",
          "hydration_sources"
        ],
        "properties": {
          "xrpc": {
            "type": "string",
            "description": "The NSID of the XRPC you wish to forward"
          },
          "atproto_proxy": {
            "type": "string",
            "description": "The destination service the request will be forwarded to"
          },
          "authorization": {
            "type": "string",
            "description": "An optional auth token to pass on\n\nthe `aud` field must match the upstream atproto_proxy service"
          },
          "atproto_accept_labelers": {
            "type": "string",
            "description": "An optional set of labelers to request be applied by the upstream"
          },
          "params": {
            "description": "The `params` for the destination service XRPC endpoint\n\nCurrently this will be passed along unchecked, but a future version of\nslingshot may attempt to do lexicon resolution to validate `params`\nbased on the upstream service"
          },
          "hydration_sources": {
            "type": "array",
            "description": "Paths within the response to look for at-uris that can be hydrated",
            "items": {
              "$ref": "#/components/schemas/HydrationSource"
            }
          }
        },
        "example": {
          "atproto_accept_labelers": null,
          "atproto_proxy": "did:web:blue.mackuba.eu#bsky_fg",
          "authorization": null,
          "hydration_sources": [
            {
              "path": "feed[].post",
              "shape": "at-uri"
            }
          ],
          "params": {
            "feed": "at://did:plc:oio4hkxaop4ao4wz2pp3f4cr/app.bsky.feed.generator/atproto"
          },
          "xrpc": "app.bsky.feed.getFeedSkeleton"
        }
      },
      "ServiceResponseObject": {
        "type": "object",
        "title": "ServiceResponseObject",
        "required": [
          "endpoint"
        ],
        "properties": {
          "endpoint": {
            "type": "string",
            "description": "The service endpoint URL, if found"
          }
        },
        "example": {
          "endpoint": "https://example.com"
        }
      },
      "XrpcErrorResponseObject": {
        "type": "object",
        "title": "XrpcErrorResponseObject",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Should correspond an error `name` in the lexicon errors array"
          },
          "message": {
            "type": "string",
            "description": "Human-readable description and possibly additonal context"
          }
        },
        "example": {
          "error": "RecordNotFound",
          "message": "This record was deleted"
        }
      }
    }
  },
  "externalDocs": {
    "url": "https://microcosm.blue/slingshot"
  }
}