├── .github ├── dependabot.yml ├── linters │ └── .openapirc.yml └── workflows │ ├── generated-pr.yml │ ├── linter.yml │ └── stale.yml ├── .gitignore ├── .spectral.yaml ├── LICENSE ├── Makefile ├── README.md ├── docs ├── index.html └── readme.md └── ipfs-pinning-service.yaml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/linters/.openapirc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | extends: spectral:oas 4 | -------------------------------------------------------------------------------- /.github/workflows/generated-pr.yml: -------------------------------------------------------------------------------- 1 | name: Close Generated PRs 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | uses: ipdxco/unified-github-workflows/.github/workflows/reusable-generated-pr.yml@v1 15 | -------------------------------------------------------------------------------- /.github/workflows/linter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: lint 3 | on: 4 | push: 5 | 6 | jobs: 7 | super-linter: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout Code 11 | uses: actions/checkout@v3 12 | - name: Validate OpenAPI 13 | uses: docker://github/super-linter:v4 14 | env: 15 | VALIDATE_ALL_CODEBASE: true 16 | VALIDATE_OPENAPI: true 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | spectral-cli: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Checkout Code 22 | uses: actions/checkout@v3 23 | - uses: actions/setup-node@v3 24 | with: 25 | node-version: 'lts/*' 26 | - run: npx @stoplight/spectral-cli lint ./ipfs-pinning-service.yaml 27 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close Stale Issues 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | uses: ipdxco/unified-github-workflows/.github/workflows/reusable-stale-issue.yml@v1 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | super-linter.log 2 | -------------------------------------------------------------------------------- /.spectral.yaml: -------------------------------------------------------------------------------- 1 | extends: spectral:oas 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: lint-spectral lint-openapi 2 | 3 | 4 | lint-spectral: 5 | npx @stoplight/spectral-cli lint ./ipfs-pinning-service.yaml 6 | lint-openapi: 7 | docker run --rm -e RUN_LOCAL=true -e VALIDATE_OPENAPI=true -v $(shell pwd):/tmp/lint github/super-linter:v4 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pinning Service API Spec 2 | 3 | [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) 4 | [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.io/) 5 | [![](https://github.com/ipfs/pinning-services-api-spec/workflows/Lint/badge.svg?branch=main)](https://github.com/ipfs/pinning-services-api-spec/actions?query=workflow%3ALint+branch%3Amain) 6 | [![](https://img.shields.io/badge/status-stable-brightgreen.svg?style=flat-square)](https://github.com/ipfs/specs/#understanding-the-meaning-of-the-spec-badges-and-their-lifecycle) 7 | 8 | > This repository contains the specs for the vendor-agnostic pinning service API for the IPFS ecosystem 9 | 10 | [![pinning-services-api-contex.png](https://user-images.githubusercontent.com/157609/108572745-438fc300-7313-11eb-93c3-c8b29c0da988.png)](#about) 11 | 12 | - [About](#about) 13 | - [Specification](#specification) 14 | - [Code generation](#code-generation) (client/server) 15 | - [Adoption](#adoption) 16 | - [Client libraries](#client-libraries) 17 | - [Server implementations](#server-implementations) 18 | - [Online services](#online-services) 19 | - [Contribute](#contribute) 20 | 21 | ## About 22 | 23 | A **pinning service** is a service that accepts [CIDs](https://github.com/ipld/cid/) from a user in order to host the data associated with them. 24 | 25 | The rationale behind defining a generic pinning service API is to have a baseline functionality and interface that can be provided by pinning services, so that tools can be built on top of a common base of functionality. 26 | 27 | In [this presentation](https://youtu.be/Pcv8Bt4HMVU), IPFS creator Juan Benet discusses current and potential pinning use cases, and how a standardized IPFS pinning API can meet these envisioned needs. 28 | 29 | The API spec in this repo is the first step towards that future. 30 | 31 | ## Specification 32 | 33 | This API is defined as an OpenAPI spec in YAML format: 34 | 35 | * **[ipfs-pinning-service.yaml](./ipfs-pinning-service.yaml)** ![](https://img.shields.io/badge/status-stable-brightgreen.svg?style=flat-square) 36 | 37 | 38 | ### Documentation 39 | 40 | You can find human-readable API documentation generated from the YAML file here: 41 | 42 | - **[https://ipfs.github.io/pinning-services-api-spec](https://ipfs.github.io/pinning-services-api-spec/)** ![](https://img.shields.io/badge/status-stable-brightgreen.svg?style=flat-square) 43 | 44 | ### Code generation 45 | 46 | https://openapi-generator.tech allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically, given the OpenAPI spec at [ipfs-pinning-service.yaml](./ipfs-pinning-service.yaml). 47 | 48 | Give it a try before you resort to implementing things from scratch. 49 | 50 | ## Adoption 51 | 52 | Built-in support for pinning services exposing this API is coming to IPFS tooling: 53 | - [go-ipfs](https://github.com/ipfs/go-ipfs) ![](https://img.shields.io/badge/status-stable-brightgreen.svg?style=flat-square) (since [v0.8.0](https://github.com/ipfs/go-ipfs/releases/v0.8.0): `ipfs pin remote --help`, see how to [work with remote pinning services](https://docs.ipfs.io/how-to/work-with-pinning-services/)) 54 | - [js-ipfs-http-client](https://www.npmjs.com/package/ipfs-http-client) ![](https://img.shields.io/badge/status-stable-brightgreen.svg?style=flat-square) (`ipfs.pin.remote.*` JS APIs) 55 | - [ipfs-cluster](https://cluster.ipfs.io) ![](https://img.shields.io/badge/status-wip-orange.svg?style=flat-square) ([issue](https://github.com/ipfs/ipfs-cluster/issues/1213)) 56 | - [js-ipfs](https://github.com/ipfs/js-ipfs#readme) – ![](https://img.shields.io/badge/status-wip-orange.svg?style=flat-square) ([js-ipfs/pull/3588](https://github.com/ipfs/js-ipfs/pull/3588)) 57 | - [ipfs-webui](https://github.com/ipfs-shipyard/ipfs-webui) ![](https://img.shields.io/badge/status-reliable-green.svg?style=flat-square) (remote pin support since [v2.12.0](https://github.com/ipfs/ipfs-webui/releases/v2.12.0)) 58 | - [ipfs-desktop](https://github.com/ipfs-shipyard/ipfs-desktop) ![](https://img.shields.io/badge/status-reliable-green.svg?style=flat-square) (>0.20.x) 59 | 60 | ### Client libraries 61 | - [js-pinning-service-http-client](https://github.com/ipfs-shipyard/js-pinning-service-http-client/) ![](https://img.shields.io/badge/status-wip-orange.svg?style=flat-square) 62 | An IPFS Pinning Service HTTP Client library for JS, used in [compliance test suite](https://github.com/ipfs/pinning-services-api-spec/issues/64). 63 | - [go-pinning-service-http-client](https://github.com/ipfs/go-pinning-service-http-client) ![](https://img.shields.io/badge/status-stable-brightgreen.svg?style=flat-square) 64 | An IPFS Pinning Service HTTP Client library for Go, used by go-ipfs internally in `ipfs pin remote --help` commands. 65 | - https://openapi-generator.tech/docs/generators#client-generators ![](https://img.shields.io/badge/status-reliable-green.svg?style=flat-square) 66 | Use [YAML file](./ipfs-pinning-service.yaml) to generate client for your language 67 | - [auspinner](https://github.com/2color/auspinner) 68 | A stateless CLI tool to pin and serve CAR files to IPFS pinning services using HTTP and Bitswap. 69 | 70 | ### Server implementations 71 | - https://github.com/ipfs/ipfs-cluster ![](https://img.shields.io/badge/status-wip-orange.svg?style=flat-square) 72 | Pinset orchestration for IPFS – [tracking issue](https://github.com/ipfs/ipfs-cluster/issues/1213) 73 | - https://github.com/ipfs-shipyard/js-mock-ipfs-pinning-service ![](https://img.shields.io/badge/status-reliable-green.svg?style=flat-square) 74 | Implementation of in-memory service for testing purposes 75 | - https://github.com/ipfs-shipyard/rb-pinning-service-api ![](https://img.shields.io/badge/status-reliable-green.svg?style=flat-square) 76 | A Rails app that implements the IPFS Pinning Service API 77 | - https://openapi-generator.tech/docs/generators#server-generators ![](https://img.shields.io/badge/status-reliable-green.svg?style=flat-square) 78 | Use [YAML file](./ipfs-pinning-service.yaml) to generate server boilerplate for your language 79 | 80 | ### CI/CD 81 | 82 | - https://github.com/marketplace/actions/ipfs-remote-pinning 83 | IPFS Pinning GitHub Action that adds data to IPFS and pins it to any `ENDPOINT` compatible with Pinning Service API 84 | 85 | ### Online services 86 | 87 | - https://pinata.cloud – ([documentation](https://pinata.cloud/documentation#PinningServicesAPI)) 88 | - `ipfs pin remote service add pinata https://api.pinata.cloud/psa YOUR_JWT` 89 | - https://nft.storage 90 | - `ipfs pin remote service add nft-storage https://nft.storage/api YOUR_API_KEY` 91 | - https://filebase.com - ([documentation](https://docs.filebase.com/storage-networks/ipfs/ipfs-pinning) + [IPFS Pin Sync](https://docs.filebase.com/ipfs/ipfs-pin-sync)) 92 | - `ipfs pin remote service add filebase https://api.filebase.io/v1/ipfs SECRET-ACCESS-TOKEN` 93 | - `{your project could be here}` – open a PR! 94 | 95 | ### Timeline 96 | 97 | - 2022 Q3 98 | - [IPFS Pin Sync](https://docs.filebase.com/ipfs/ipfs-pin-sync) is announced by Filebase 99 | - 2022 Q1 100 | - [web3.storage](https://web3.storage) API support: https://docs.web3.storage/how-tos/pinning-services-api 101 | - [estuary.tech](https://estuary.tech) API support: https://docs.estuary.tech/pinning-list 102 | - Mock server for local development: https://github.com/ipfs-shipyard/js-mock-ipfs-pinning-service 103 | - WIP official API client for JS: https://github.com/ipfs-shipyard/js-pinning-service-http-client/ 104 | - WIP compliance test suite: https://github.com/ipfs/pinning-services-api-spec/issues/64 105 | - WIP ipfs-cluster support ([commit](https://github.com/ipfs/ipfs-cluster/commit/9549e0c86e500a0b15020f6e5d48664d1f3ab37d)) 106 | - 2021 Q1 107 | - [go-ipfs 0.8.0](https://github.com/ipfs/go-ipfs/releases/v0.8.0) shipped with built-in client for v1.0.0 of this API 108 | - Pinata announces endpoint compatible with this spec: https://pinata.cloud/documentation#PinningServicesAPI 109 | - ipfs-webui [v2.12.0](https://github.com/ipfs/ipfs-webui/releases/v2.12.0) provides UI based on `pin remote` commands 110 | - Textile is [working on Bucket Pinning API](https://github.com/textileio/textile/discussions/499) 111 | - 2020 Q3 112 | - IPFS GUI WG working on adding support for pinning services into IPFS Desktop/Web UI: 113 | - [Epic: Pinning service integration · Issue #91 · ipfs/ipfs-gui](https://github.com/ipfs/ipfs-gui/issues/91) 114 | - [Analysis of remote pinning services vs the needs of IPFS Web UI](https://docs.google.com/document/d/1f0R7woLtW_YTv9P9IOrUNK6QafgctJ7qTggEUdepD_c/) 115 | - [ipfs/pinning-services-api-specs](https://github.com/ipfs/pinning-services-api-specs) is created as a place for stakeholders to collaborate and finalize the API 116 | - 2020-07-14: Spec in draft status is ready for implementation 117 | - 2020-08: Addressing feedback from early implementers 118 | - 2020-09: End-to-end testing 119 | - 2020 Q2 120 | - Pinning Summit 2020 ([website](https://ipfspinningsummit.com/), [recorded talks](https://www.youtube.com/watch?v=rYD2lfuatJM&list=PLuhRWgmPaHtTvsxuZ9T-tMlu_v0lja6v5)) 121 | - 2019 Q2 122 | - Creation of a generic pinning service API proposed in [ipfs/notes/issues/378](https://github.com/ipfs/notes/issues/378) 123 | 124 | ## Contribute 125 | 126 | Suggestions, contributions, and criticisms are welcome! However, please make sure to familiarize yourself deeply with IPFS, the models it adopts, and the principles it follows. 127 | 128 | This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). 129 | 130 | ### Spec lifecycle 131 | 132 | We use the following label system to identify the state of aspects of this spec: 133 | 134 | - ![](https://img.shields.io/badge/status-wip-orange.svg?style=flat-square) — A work-in-progress, possibly to describe an idea before actually committing to a full draft of the spec 135 | - ![](https://img.shields.io/badge/status-draft-yellow.svg?style=flat-square) — A draft that is ready to review, and should be implementable 136 | - ![](https://img.shields.io/badge/status-reliable-green.svg?style=flat-square) — A spec that has been adopted (implemented) and can be used as a reference to learn how the system works 137 | - ![](https://img.shields.io/badge/status-stable-brightgreen.svg?style=flat-square) — We consider this spec to close to final; it might be improved, but the system it specifies should not fundamentally change 138 | - ![](https://img.shields.io/badge/status-permanent-blue.svg?style=flat-square) — This spec will not change 139 | - ![](https://img.shields.io/badge/status-deprecated-red.svg?style=flat-square) — This spec is no longer in use 140 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IPFS Pinning Service API 5 | 6 | 7 | 8 | 9 | 10 | 176 | 177 | 178 |
179 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /index.html 3 | --- 4 | -------------------------------------------------------------------------------- /ipfs-pinning-service.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.3 2 | info: 3 | version: "1.0.0" 4 | title: 'IPFS Pinning Service API' 5 | x-logo: 6 | url: "https://bafybeidehxarrk54mkgyl5yxbgjzqilp6tkaz2or36jhq24n3rdtuven54.ipfs.dweb.link/?filename=ipfs-pinning-service.svg" 7 | contact: 8 | name: IPFS 9 | url: https://github.com/ipfs/pinning-services-api-spec 10 | description: " 11 | 12 | 13 | ## About this spec 14 | 15 | The IPFS Pinning Service API is intended to be an implementation-agnostic API: 16 | 17 | - For use and implementation by pinning service providers 18 | 19 | - For use in client mode by IPFS nodes and GUI-based applications 20 | 21 | 22 | ### Document scope and intended audience 23 | 24 | The intended audience of this document is **IPFS developers** building pinning service clients or servers compatible with this OpenAPI spec. 25 | Your input and feedback are welcome and valuable as we develop this API spec. Please join the design discussion at [github.com/ipfs/pinning-services-api-spec](https://github.com/ipfs/pinning-services-api-spec). 26 | 27 | 28 | **IPFS users** should see the tutorial at [docs.ipfs.io/how-to/work-with-pinning-services](https://docs.ipfs.io/how-to/work-with-pinning-services/) instead. 29 | 30 | 31 | ### Related resources 32 | 33 | The latest version of this spec and additional resources can be found at: 34 | 35 | - Specification: https://github.com/ipfs/pinning-services-api-spec/raw/main/ipfs-pinning-service.yaml 36 | 37 | - Docs: https://ipfs.github.io/pinning-services-api-spec/ 38 | 39 | - Clients and services: https://github.com/ipfs/pinning-services-api-spec#adoption 40 | 41 | 42 | # Schemas 43 | 44 | This section describes the most important object types and conventions. 45 | 46 | 47 | A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml). 48 | 49 | 50 | ## Identifiers 51 | 52 | ### cid 53 | 54 | [Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively. 55 | 56 | ### requestid 57 | 58 | Unique identifier of a pin request. 59 | 60 | 61 | When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests. 62 | 63 | 64 | Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions. 65 | 66 | 67 | ## Objects 68 | 69 | ### Pin object 70 | 71 | 72 | ![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png) 73 | 74 | 75 | The `Pin` object is a representation of a pin request. 76 | 77 | 78 | It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`. 79 | Addresses provided in `origins` list are relevant only during the initial pinning, and don't need to be persisted by the pinning service. 80 | 81 | 82 | ### Pin status response 83 | 84 | 85 | ![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png) 86 | 87 | 88 | The `PinStatus` object is a representation of the current state of a pinning operation. 89 | 90 | It includes values from the original `Pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. 91 | Addresses in the `delegates` array are peers designated by pinning service that will receive the pin data over bitswap (more details in the [Provider hints](#section/Provider-hints) section). Any additional vendor-specific information is returned in optional `info`. 92 | 93 | 94 | # The pin lifecycle 95 | 96 | 97 | ![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png) 98 | 99 | 100 | ## Creating a new pin object 101 | 102 | The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response: 103 | 104 | - `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future 105 | 106 | - `status` in `PinStatus` indicates the current state of a pin 107 | 108 | 109 | ## Checking status of in-progress pinning 110 | 111 | `status` (in `PinStatus`) may indicate one of the two pending states: `queued` or `pinning`: 112 | 113 | - `queued` is passive: the pin was added to the queue but the service isn't consuming any resources to retrieve it yet. 114 | - `pinning` is active: the pinning service is trying to retrieve the CIDs by finding providers for all involved CIDs, connect to these providers and download data from them. 115 | 116 | When a new pin object is created it typically starts in a `queued` state. Once the pinning service actively seeks to retrieve the file it changes to `pinning`. `pinning` typically means that the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time. 117 | 118 | In either case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin. 119 | 120 | ## Replacing an existing pin object 121 | 122 | The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically. 123 | 124 | 125 | ## Removing a pin object 126 | 127 | A pin object can be removed via `DELETE /pins/{requestid}`. 128 | 129 | 130 | 131 | # Provider hints 132 | 133 | Provider hints take the form of two [multiaddr](https://docs.ipfs.io/concepts/glossary/#multiaddr) lists: `Pin.origins` and `PinStatus.delegates` 134 | 135 | 136 | ## Pin.origins 137 | 138 | A list of known sources (providers) of the data. Sent by a client in a pin request. 139 | Pinning service will try to connect to them to speed up data transfer. 140 | 141 | 142 | ## PinStatus.delegates 143 | 144 | A list of temporary destination (retrievers) for the data. Returned by pinning service in a response for a pin request. 145 | These peers are provided by a pinning service for the purpose of fetching data about to be pinned. 146 | 147 | 148 | ## Optimizing for speed and connectivity 149 | 150 | Both ends should attempt to preconnect to each other: 151 | 152 | - Delegates should always preconnect to origins 153 | 154 | - Clients who initiate pin request and also have the pinned data in their own local datastore should preconnect to delegates 155 | 156 | 157 | **NOTE:** Connections to multiaddrs in `origins` and `delegates` arrays should 158 | be attempted in best-effort fashion, and dial failure should not fail the 159 | pinning operation. When unable to act on explicit provider hints, DHT and 160 | other discovery methods should be used as a fallback by a pinning service. 161 | 162 | 163 | ## Rationale 164 | 165 | A pinning service will use the DHT and other discovery methods to locate pinned 166 | content; however, it may not be able to retrieve data if the only provider 167 | has no publicly diallable address (e.g. a desktop peer behind a restrictive NAT/firewall). 168 | 169 | 170 | Leveraging provider hints mitigates potential connectivity issues and speeds up the content routing phase. 171 | If a client has the data in their own datastore or already knows of other providers, the transfer will start immediately. 172 | 173 | 174 | The most common scenario is a client putting its own IPFS node's multiaddrs in 175 | `Pin.origins`, and then attempt to connect to every multiaddr returned by a 176 | pinning service in `PinStatus.delegates` to initiate transfer. At the same 177 | time, a pinning service will try to connect to multiaddrs provided by the client 178 | in `Pin.origins`. 179 | 180 | 181 | This ensures data transfer starts immediately (without waiting for provider 182 | discovery over DHT), and mutual direct dial between a client and a service 183 | works around peer routing issues in restrictive network topologies, such as 184 | NATs, firewalls, etc. 185 | 186 | 187 | **NOTE:** All multiaddrs MUST end with `/p2p/{peerID}` and SHOULD be fully 188 | resolved and confirmed to be dialable from the public internet. Avoid sending 189 | addresses from local networks. 190 | 191 | 192 | # Custom metadata 193 | 194 | Pinning services are encouraged to add support for additional features by leveraging the optional `Pin.meta` and `PinStatus.info` fields. 195 | While these attributes can be application- or vendor-specific, we encourage the community at large to leverage these attributes as a sandbox to come up with conventions that could become part of future revisions of this API. 196 | 197 | ## Pin metadata 198 | 199 | String keys and values passed in `Pin.meta` are persisted with the pin object. 200 | This is an opt-in feature: It is OK for a client to omit or ignore these optional attributes, and doing so should not impact the basic pinning functionality. 201 | 202 | 203 | Potential uses: 204 | 205 | - `Pin.meta[app_id]`: Attaching a unique identifier to pins created by an app enables meta-filtering pins per app 206 | 207 | - `Pin.meta[vendor_policy]`: Vendor-specific policy (for example: which region to use, how many copies to keep) 208 | 209 | 210 | ### Filtering based on metadata 211 | 212 | The contents of `Pin.meta` can be used as an advanced search filter for situations where searching by `name` and `cid` is not enough. 213 | 214 | 215 | Metadata key matching rule is `AND`: 216 | 217 | - lookup returns pins that have `meta` with all key-value pairs matching the passed values 218 | 219 | - pin metadata may have more keys, but only ones passed in the query are used for filtering 220 | 221 | 222 | The wire format for the `meta` when used as a query parameter is a [URL-escaped](https://en.wikipedia.org/wiki/Percent-encoding) stringified JSON object. 223 | A lookup example for pins that have a `meta` key-value pair `{\"app_id\":\"UUID\"}` is: 224 | 225 | - `GET /pins?meta=%7B%22app_id%22%3A%22UUID%22%7D` 226 | 227 | 228 | 229 | ## Pin status info 230 | 231 | Additional `PinStatus.info` can be returned by pinning service. 232 | 233 | 234 | Potential uses: 235 | 236 | - `PinStatus.info[status_details]`: more info about the current status (queue 237 | position, percentage of transferred data, summary of where data is stored, etc); when 238 | `PinStatus.status=failed`, it could provide a reason why a pin operation 239 | failed (e.g. lack of funds, DAG too big, etc.) 240 | 241 | - `PinStatus.info[dag_size]`: the size of pinned data, along with DAG overhead 242 | 243 | - `PinStatus.info[raw_size]`: the size of data without DAG overhead (eg. unixfs) 244 | 245 | - `PinStatus.info[pinned_until]`: if vendor supports time-bound pins, this could indicate when the pin will expire 246 | 247 | 248 | # Pagination and filtering 249 | 250 | Pin objects can be listed by executing `GET /pins` with optional parameters: 251 | 252 | 253 | - When no filters are provided, the endpoint will return a small batch of the 10 most 254 | recently created items, from the latest to the oldest. 255 | 256 | - The number of returned items can be adjusted with the `limit` parameter 257 | (implicit default is 10). 258 | 259 | - If the value in `PinResults.count` is bigger than the length of 260 | `PinResults.results`, the client can infer there are more results that can be queried. 261 | 262 | - To read more items, pass the `before` filter with the timestamp from 263 | `PinStatus.created` found in the oldest item in the current batch of results. 264 | Repeat to read all results. 265 | 266 | - Returned results can be fine-tuned by applying optional `after`, `cid`, `name`, 267 | `status`, or `meta` filters. 268 | 269 | 270 | > **Note**: pagination by the `created` timestamp requires each value to be 271 | globally unique. Any future considerations to add support for bulk creation 272 | must account for this. 273 | 274 | 275 | " 276 | 277 | servers: 278 | - url: https://pinning-service.example.com 279 | 280 | tags: 281 | - name: pins 282 | 283 | paths: 284 | /pins: 285 | get: 286 | operationId: getPins 287 | summary: List pin objects 288 | description: List all the pin objects, matching optional filters; when no filter is provided, only successful pins are returned 289 | tags: 290 | - pins 291 | parameters: 292 | - $ref: '#/components/parameters/cid' 293 | - $ref: '#/components/parameters/name' 294 | - $ref: '#/components/parameters/match' 295 | - $ref: '#/components/parameters/status' 296 | - $ref: '#/components/parameters/before' 297 | - $ref: '#/components/parameters/after' 298 | - $ref: '#/components/parameters/limit' 299 | - $ref: '#/components/parameters/meta' 300 | responses: 301 | '200': 302 | description: Successful response (PinResults object) 303 | content: 304 | application/json: 305 | schema: 306 | $ref: '#/components/schemas/PinResults' 307 | '400': 308 | $ref: '#/components/responses/BadRequest' 309 | '401': 310 | $ref: '#/components/responses/Unauthorized' 311 | '404': 312 | $ref: '#/components/responses/NotFound' 313 | '409': 314 | $ref: '#/components/responses/InsufficientFunds' 315 | '4XX': 316 | $ref: '#/components/responses/CustomServiceError' 317 | '5XX': 318 | $ref: '#/components/responses/InternalServerError' 319 | post: 320 | operationId: addPin 321 | summary: Add pin object 322 | description: Add a new pin object for the current access token 323 | tags: 324 | - pins 325 | requestBody: 326 | required: true 327 | content: 328 | application/json: 329 | schema: 330 | $ref: '#/components/schemas/Pin' 331 | responses: 332 | '202': 333 | description: Successful response (PinStatus object) 334 | content: 335 | application/json: 336 | schema: 337 | $ref: '#/components/schemas/PinStatus' 338 | '400': 339 | $ref: '#/components/responses/BadRequest' 340 | '401': 341 | $ref: '#/components/responses/Unauthorized' 342 | '404': 343 | $ref: '#/components/responses/NotFound' 344 | '409': 345 | $ref: '#/components/responses/InsufficientFunds' 346 | '4XX': 347 | $ref: '#/components/responses/CustomServiceError' 348 | '5XX': 349 | $ref: '#/components/responses/InternalServerError' 350 | 351 | /pins/{requestid}: 352 | parameters: 353 | - name: requestid 354 | in: path 355 | required: true 356 | schema: 357 | type: string 358 | get: 359 | operationId: getPinByRequestId 360 | summary: Get pin object 361 | description: Get a pin object and its status 362 | tags: 363 | - pins 364 | responses: 365 | '200': 366 | description: Successful response (PinStatus object) 367 | content: 368 | application/json: 369 | schema: 370 | $ref: '#/components/schemas/PinStatus' 371 | '400': 372 | $ref: '#/components/responses/BadRequest' 373 | '401': 374 | $ref: '#/components/responses/Unauthorized' 375 | '404': 376 | $ref: '#/components/responses/NotFound' 377 | '409': 378 | $ref: '#/components/responses/InsufficientFunds' 379 | '4XX': 380 | $ref: '#/components/responses/CustomServiceError' 381 | '5XX': 382 | $ref: '#/components/responses/InternalServerError' 383 | post: 384 | operationId: replacePinByRequestId 385 | summary: Replace pin object 386 | description: Replace an existing pin object (shortcut for executing remove and add operations in one step to avoid unnecessary garbage collection of blocks present in both recursive pins) 387 | tags: 388 | - pins 389 | requestBody: 390 | required: true 391 | content: 392 | application/json: 393 | schema: 394 | $ref: '#/components/schemas/Pin' 395 | responses: 396 | '202': 397 | description: Successful response (PinStatus object) 398 | content: 399 | application/json: 400 | schema: 401 | $ref: '#/components/schemas/PinStatus' 402 | '400': 403 | $ref: '#/components/responses/BadRequest' 404 | '401': 405 | $ref: '#/components/responses/Unauthorized' 406 | '404': 407 | $ref: '#/components/responses/NotFound' 408 | '409': 409 | $ref: '#/components/responses/InsufficientFunds' 410 | '4XX': 411 | $ref: '#/components/responses/CustomServiceError' 412 | '5XX': 413 | $ref: '#/components/responses/InternalServerError' 414 | delete: 415 | operationId: deletePinByRequestId 416 | summary: Remove pin object 417 | description: Remove a pin object 418 | tags: 419 | - pins 420 | responses: 421 | '202': 422 | description: Successful response (no body, pin removed) 423 | '400': 424 | $ref: '#/components/responses/BadRequest' 425 | '401': 426 | $ref: '#/components/responses/Unauthorized' 427 | '404': 428 | $ref: '#/components/responses/NotFound' 429 | '409': 430 | $ref: '#/components/responses/InsufficientFunds' 431 | '4XX': 432 | $ref: '#/components/responses/CustomServiceError' 433 | '5XX': 434 | $ref: '#/components/responses/InternalServerError' 435 | 436 | components: 437 | schemas: 438 | 439 | PinResults: 440 | description: Response used for listing pin objects matching request 441 | type: object 442 | required: 443 | - count 444 | - results 445 | properties: 446 | count: 447 | description: The total number of pin objects that exist for passed query filters 448 | type: integer 449 | format: int32 450 | minimum: 0 451 | example: 1 452 | results: 453 | description: An array of PinStatus results 454 | type: array 455 | items: 456 | $ref: '#/components/schemas/PinStatus' 457 | uniqueItems: true 458 | minItems: 0 459 | maxItems: 1000 460 | 461 | PinStatus: 462 | description: Pin object with status 463 | type: object 464 | required: 465 | - requestid 466 | - status 467 | - created 468 | - pin 469 | - delegates 470 | properties: 471 | requestid: 472 | description: Globally unique identifier of the pin request; can be used to check the status of ongoing pinning, or pin removal 473 | type: string 474 | example: "UniqueIdOfPinRequest" 475 | status: 476 | $ref: '#/components/schemas/Status' 477 | created: 478 | description: Immutable timestamp indicating when a pin request entered a pinning service; can be used for filtering results and pagination 479 | type: string 480 | format: date-time # RFC 3339, section 5.6 481 | example: "2020-07-27T17:32:28.276Z" 482 | pin: 483 | $ref: '#/components/schemas/Pin' 484 | delegates: 485 | $ref: '#/components/schemas/Delegates' 486 | info: 487 | $ref: '#/components/schemas/StatusInfo' 488 | 489 | Pin: 490 | description: Pin object 491 | type: object 492 | required: 493 | - cid 494 | properties: 495 | cid: 496 | description: Content Identifier (CID) to be pinned recursively 497 | type: string 498 | example: "QmCIDToBePinned" 499 | name: 500 | description: Optional name for pinned data; can be used for lookups later 501 | type: string 502 | maxLength: 255 503 | example: "PreciousData.pdf" 504 | origins: 505 | $ref: '#/components/schemas/Origins' 506 | meta: 507 | $ref: '#/components/schemas/PinMeta' 508 | 509 | Status: 510 | description: Status a pin object can have at a pinning service 511 | type: string 512 | enum: 513 | - queued # pinning operation is waiting in the queue; additional info can be returned in info[status_details] 514 | - pinning # pinning in progress; additional info can be returned in info[status_details] 515 | - pinned # pinned successfully 516 | - failed # pinning service was unable to finish pinning operation; additional info can be found in info[status_details] 517 | 518 | Delegates: 519 | description: List of multiaddrs designated by pinning service that will receive the pin data; see Provider Hints in the docs 520 | type: array 521 | items: 522 | type: string 523 | uniqueItems: true 524 | minItems: 1 525 | maxItems: 20 526 | example: ['/ip4/203.0.113.1/tcp/4001/p2p/QmServicePeerId'] 527 | 528 | Origins: 529 | description: Optional list of multiaddrs known to provide the data; see Provider Hints in the docs 530 | type: array 531 | items: 532 | type: string 533 | uniqueItems: true 534 | minItems: 0 535 | maxItems: 20 536 | example: ['/ip4/203.0.113.142/tcp/4001/p2p/QmSourcePeerId', '/ip4/203.0.113.114/udp/4001/quic/p2p/QmSourcePeerId'] 537 | 538 | PinMeta: 539 | description: Optional metadata for pin object 540 | type: object 541 | additionalProperties: 542 | type: string 543 | minProperties: 0 544 | maxProperties: 1000 545 | example: 546 | app_id: "99986338-1113-4706-8302-4420da6158aa" # Pin.meta[app_id], useful for filtering pins per app 547 | 548 | StatusInfo: 549 | description: Optional info for PinStatus response 550 | type: object 551 | additionalProperties: 552 | type: string 553 | minProperties: 0 554 | maxProperties: 1000 555 | example: 556 | status_details: "Queue position: 7 of 9" # PinStatus.info[status_details], when status=queued 557 | 558 | TextMatchingStrategy: 559 | description: Alternative text matching strategy 560 | type: string 561 | default: exact 562 | enum: 563 | - exact # full match, case-sensitive (the implicit default) 564 | - iexact # full match, case-insensitive 565 | - partial # partial match, case-sensitive 566 | - ipartial # partial match, case-insensitive 567 | 568 | Failure: 569 | description: Response for a failed request 570 | type: object 571 | required: 572 | - error 573 | properties: 574 | error: 575 | type: object 576 | required: 577 | - reason 578 | properties: 579 | reason: 580 | type: string 581 | description: Mandatory string identifying the type of error 582 | example: "ERROR_CODE_FOR_MACHINES" 583 | details: 584 | type: string 585 | description: Optional, longer description of the error; may include UUID of transaction for support, links to documentation etc 586 | example: "Optional explanation for humans with more details" 587 | 588 | parameters: 589 | 590 | before: 591 | description: Return results created (queued) before provided timestamp 592 | name: before 593 | in: query 594 | required: false 595 | schema: 596 | type: string 597 | format: date-time # RFC 3339, section 5.6 598 | example: "2020-07-27T17:32:28.276Z" 599 | 600 | after: 601 | description: Return results created (queued) after provided timestamp 602 | name: after 603 | in: query 604 | required: false 605 | schema: 606 | type: string 607 | format: date-time # RFC 3339, section 5.6 608 | example: "2020-07-27T17:32:28.276Z" 609 | 610 | limit: 611 | description: Max records to return 612 | name: limit 613 | in: query 614 | required: false 615 | schema: 616 | type: integer 617 | format: int32 618 | minimum: 1 619 | maximum: 1000 620 | default: 10 621 | 622 | cid: 623 | description: Return pin objects responsible for pinning the specified CID(s); be aware that using longer hash functions introduces further constraints on the number of CIDs that will fit under the limit of 2000 characters per URL in browser contexts 624 | name: cid 625 | in: query 626 | required: false 627 | schema: 628 | type: array 629 | items: 630 | type: string 631 | uniqueItems: true 632 | minItems: 1 633 | maxItems: 10 634 | style: form # ?cid=Qm1,Qm2,bafy3 635 | explode: false 636 | example: ["Qm1","Qm2","bafy3"] 637 | 638 | name: 639 | description: Return pin objects with specified name (by default a case-sensitive, exact match) 640 | name: name 641 | in: query 642 | required: false 643 | schema: 644 | type: string 645 | maxLength: 255 646 | example: "PreciousData.pdf" 647 | 648 | match: 649 | description: Customize the text matching strategy applied when the name filter is present; exact (the default) is a case-sensitive exact match, partial matches anywhere in the name, iexact and ipartial are case-insensitive versions of the exact and partial strategies 650 | name: match 651 | in: query 652 | required: false 653 | schema: 654 | $ref: '#/components/schemas/TextMatchingStrategy' 655 | example: exact 656 | 657 | status: 658 | description: Return pin objects for pins with the specified status (when missing, service should default to pinned only) 659 | name: status 660 | in: query 661 | required: false 662 | schema: 663 | type: array 664 | items: 665 | $ref: '#/components/schemas/Status' 666 | uniqueItems: true 667 | minItems: 1 668 | style: form # ?status=queued,pinning 669 | explode: false 670 | example: ["queued","pinning"] 671 | 672 | meta: 673 | description: Return pin objects that match specified metadata keys passed as a string representation of a JSON object; when implementing a client library, make sure the parameter is URL-encoded to ensure safe transport 674 | name: meta 675 | in: query 676 | required: false 677 | content: 678 | application/json: # ?meta={"foo":"bar"} 679 | schema: 680 | $ref: '#/components/schemas/PinMeta' 681 | 682 | responses: 683 | 684 | BadRequest: 685 | description: Error response (Bad request) 686 | content: 687 | application/json: 688 | schema: 689 | $ref: '#/components/schemas/Failure' 690 | examples: 691 | BadRequestExample: 692 | $ref: '#/components/examples/BadRequestExample' 693 | 694 | Unauthorized: 695 | description: Error response (Unauthorized; access token is missing or invalid) 696 | content: 697 | application/json: 698 | schema: 699 | $ref: '#/components/schemas/Failure' 700 | examples: 701 | UnauthorizedExample: 702 | $ref: '#/components/examples/UnauthorizedExample' 703 | 704 | NotFound: 705 | description: Error response (The specified resource was not found) 706 | content: 707 | application/json: 708 | schema: 709 | $ref: '#/components/schemas/Failure' 710 | examples: 711 | NotFoundExample: 712 | $ref: '#/components/examples/NotFoundExample' 713 | 714 | InsufficientFunds: 715 | description: Error response (Insufficient funds) 716 | content: 717 | application/json: 718 | schema: 719 | $ref: '#/components/schemas/Failure' 720 | examples: 721 | InsufficientFundsExample: 722 | $ref: '#/components/examples/InsufficientFundsExample' 723 | 724 | CustomServiceError: 725 | description: Error response (Custom service error) 726 | content: 727 | application/json: 728 | schema: 729 | $ref: '#/components/schemas/Failure' 730 | examples: 731 | CustomServiceErrorExample: 732 | $ref: '#/components/examples/CustomServiceErrorExample' 733 | 734 | InternalServerError: 735 | description: Error response (Unexpected internal server error) 736 | content: 737 | application/json: 738 | schema: 739 | $ref: '#/components/schemas/Failure' 740 | examples: 741 | InternalServerErrorExample: 742 | $ref: '#/components/examples/InternalServerErrorExample' 743 | 744 | examples: 745 | 746 | BadRequestExample: 747 | value: 748 | error: 749 | reason: "BAD_REQUEST" 750 | details: "Explanation for humans with more details" 751 | summary: A sample response to a bad request; reason will differ 752 | 753 | UnauthorizedExample: 754 | value: 755 | error: 756 | reason: "UNAUTHORIZED" 757 | details: "Access token is missing or invalid" 758 | summary: Response to an unauthorized request 759 | 760 | NotFoundExample: 761 | value: 762 | error: 763 | reason: "NOT_FOUND" 764 | details: "The specified resource was not found" 765 | summary: Response to a request for a resource that does not exist 766 | 767 | InsufficientFundsExample: 768 | value: 769 | error: 770 | reason: "INSUFFICIENT_FUNDS" 771 | details: "Unable to process request due to the lack of funds" 772 | summary: Response when access token run out of funds 773 | 774 | CustomServiceErrorExample: 775 | value: 776 | error: 777 | reason: "CUSTOM_ERROR_CODE_FOR_MACHINES" 778 | details: "Optional explanation for humans with more details" 779 | summary: Response when a custom error occured 780 | 781 | InternalServerErrorExample: 782 | value: 783 | error: 784 | reason: "INTERNAL_SERVER_ERROR" 785 | details: "Explanation for humans with more details" 786 | summary: Response when unexpected error occured 787 | 788 | securitySchemes: 789 | accessToken: 790 | description: " 791 | An opaque token is required to be sent with each request in the HTTP header: 792 | 793 | - `Authorization: Bearer ` 794 | 795 | 796 | The `access-token` should be generated per device, and the user should have the ability to revoke each token separately. 797 | " 798 | type: http 799 | scheme: bearer 800 | security: 801 | - accessToken: [] 802 | --------------------------------------------------------------------------------