Get started with Pinion in three simple steps:
Sign in to your Dashboard and navigate to your profile settings to create an API token. Keep it secure!
Use curl for quick tests, or install an IPFS pinning service client library for your language of choice.
Try pinning content with a simple curl command:
curl -X POST https://<environment>.pinion.build/pinning/api/v1/pins \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"cid": "bafybeigdyrzt...", "name": "my-first-pin"}'The Pinning Service API allows you to manage and persist content on IPFS by "pinning" CID objects. Pinning ensures that specific data remains available and is not garbage-collected by IPFS nodes.
This API is compatible with the IPFS Pinning Service API specification v1.0.0.
All endpoints require authentication using a Bearer token.
Authorization: Bearer <API_TOKEN>
To create or manage API tokens, visit your Account Settings.
Retrieve a list of pinned or pinning requests.
| Name | Type | Description |
|---|---|---|
| cid | string | Filter by specific CID (exact match or comma-separated list) |
| name | string | Filter by user-defined pin name |
| status | string | Filter by pin status (queued, pinning, pinned, failed) |
| limit | integer | Maximum number of results to return |
| before | string | Return results created before this ISO 8601 timestamp |
| after | string | Return results created after this ISO 8601 timestamp |
The Pinion Gateway provides HTTP access to content stored in the IPFS blockstore by CID. It implements the IPFS HTTP Gateway specification, including full content-type negotiation via the Accept header.
Because Pinion uses a shared blockstore, content uploaded through the Upload Service is immediately retrievable through the Gateway — even before the worker has finished processing the pin request.
Two authentication methods are supported, routed at different paths:
| Method | Path prefix | Required |
|---|---|---|
| JWT Bearer token | /gateway/api/v1/ | Gateway Service group membership |
| OAuth2 session (browser) | /gateway/web/ | Active browser session cookie |
Retrieve content by CID. Optionally traverse into a UnixFS directory using a path suffix.
| Parameter | Type | Description |
|---|---|---|
| cid | path (required) | IPFS Content Identifier (CIDv0 or CIDv1) |
| path | path (optional) | Sub-path within a UnixFS directory (e.g. /index.html) |
Use the Accept header to request a specific response format:
| Accept header | Response |
|---|---|
| (default) | Deserialized content (file bytes, HTML for directories) |
| application/vnd.ipld.car | CAR archive of the DAG rooted at the CID |
| application/vnd.ipld.raw | Raw block bytes for the exact CID |
| application/json | JSON representation (for IPLD JSON nodes) |
Responses include standard HTTP caching headers. Because IPFS content is content-addressed and immutable, responses for a given CID are safe to cache indefinitely. Cache-Control: public, max-age=29030400, immutable is set by the gateway for resolved content.
Fetch a file by CID (JWT):
curl https://<environment>.pinion.build/gateway/api/v1/ipfs/bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi \ -H "Authorization: Bearer YOUR_TOKEN"
Download a CAR archive of a DAG:
curl https://<environment>.pinion.build/gateway/api/v1/ipfs/bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Accept: application/vnd.ipld.car" \ -o content.car
Fetch a file within a directory CID:
curl https://<environment>.pinion.build/gateway/api/v1/ipfs/bafybeid.../index.html \ -H "Authorization: Bearer YOUR_TOKEN"
Gateway bandwidth is metered and billed according to your subscription plan. See the Pricing page for details.
The Upload Service accepts files and CAR archives, writes them directly into the shared IPFS blockstore, and creates a pin request for asynchronous processing by the worker cluster. Content is immediately available via the Gateway upon upload — before the pin is confirmed.
Two upload formats are supported:
| Method | Path prefix | Required |
|---|---|---|
| JWT Bearer token | /upload/api/v1/ | Upload Service group membership |
| OAuth2 session (browser) | /upload/web/ | Active browser session cookie |
Upload a file or CAR archive to IPFS.
| Name | Type | Description |
|---|---|---|
| format | string | Set to car to treat the body as a CAR archive. Alternatively, set Content-Type: application/vnd.ipld.car. |
| name | string | Human-readable label for the pin. For CAR files with multiple roots, -root-0, -root-1, … are appended automatically. |
| Content-Type | Body |
|---|---|
| multipart/form-data | Form field file containing the file bytes |
| application/octet-stream | Raw file bytes in the request body |
| application/vnd.ipld.car | CAR archive bytes in the request body |
Returns a PinStatus object on success, or an array of PinStatus objects when a CAR file contains multiple roots.
// Single root
{
"requestid": "550e8400-e29b-41d4-a716-446655440000",
"status": "pinning",
"created": "2025-01-15T12:00:00Z",
"pin": {
"cid": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
"name": "my-file"
}
}After upload, the pin enters pinning status. A worker node picks up the request from the pubsub queue, verifies the content is present in the blockstore, and transitions the pin to pinned. Because content is written directly to the shared blockstore on upload, it is retrievable via the Gateway immediately — not only after pinning completes.
Upload a regular file (multipart):
curl -X POST https://<environment>.pinion.build/upload/api/v1/ \ -H "Authorization: Bearer YOUR_TOKEN" \ -F "file=@./myfile.txt" \ -G --data-urlencode "name=myfile"
Upload raw bytes (octet-stream):
curl -X POST https://<environment>.pinion.build/upload/api/v1/?name=myfile \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/octet-stream" \ --data-binary @./myfile.txt
Upload a CAR archive (via Content-Type):
curl -X POST https://<environment>.pinion.build/upload/api/v1/?name=my-dag \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/vnd.ipld.car" \ --data-binary @./content.car
Upload a CAR archive (via query param, useful when Content-Type cannot be set):
curl -X POST "https://<environment>.pinion.build/upload/api/v1/?format=car&name=my-dag" \ -H "Authorization: Bearer YOUR_TOKEN" \ --data-binary @./content.car
Pinion integrates with GitHub as a GitHub App. Once installed on your account or organization, Pinion monitors your repositories and automatically pins content to IPFS based on a configuration file you commit to each repository.
No CI pipeline changes are needed. Pinion responds directly to GitHub webhook events.
Green checkmarks on every commit
Pinion posts a commit status to GitHub under the pinion/ipfs context. When pinning succeeds, the status turns green and includes the IPFS CID. Clicking Details opens the pinned content directly in the Pinion gateway.
docs/, dist/) independently.*.tar.gz), and control whether auto-generated source code archives are included.pinion.build.yaml file to the root of any repository you want Pinion to monitor.pinion.build.yamlPlace this file at the root of your repository. Sections you omit are ignored.
version: 1
# Release asset pinning - supports pattern matching
release_assets:
enabled: true
include_source_code: true
patterns:
- "*.txt"
- "*.tar.gz"
# Directory monitoring - supports multiple paths and branch selection
subdirectories:
enabled: true
branch: "main" # Git branch to monitor (default: "main")
paths:
- "docs/" # Pin documentation directory
- "static/" # Pin static assets
- "dist/" # Pin build output
- "data/" # Pin data filesrelease_assets| Field | Type | Description |
|---|---|---|
| enabled | bool | Pin assets when a release is published |
| patterns | string[] | Glob patterns to filter which assets are pinned. If omitted, all assets are pinned. |
| include_source_code | bool | Whether to include the auto-generated source code archives (source.zip, source.tar.gz) that GitHub attaches to every release. |
subdirectories| Field | Type | Description |
|---|---|---|
| enabled | bool | Pin the listed paths on each push |
| branch | string | Branch to monitor. Defaults to main. |
| paths | string[] | Repository paths to pin. Each path is pinned independently and receives its own CID. |
Each webhook event Pinion receives is recorded with a status and any processing messages. You can review these events from your GitHub Events page to diagnose configuration issues or confirm that pins were created successfully.
The Prover API adds cryptographic proofs of storage to your pinned content. Rather than trusting that your data is safe, you can issue mathematical challenges to Pinion servers and verify the responses yourself — no Pinion involvement in the verification step. See the Storage Proofs page for a full explanation of the protocols.
A prover key is a server-held cryptographic keypair generated for a specific proof protocol. Creating a key returns a client_setup blob — the public material you need to generate challenges and verify proofs. The signing secret stays on the server. One key can cover many pinned CIDs simultaneously.
| Field | Type | Description |
|---|---|---|
| key_id | string (UUID) | Unique identifier for this key. Pass it to /prove and tagging endpoints. |
| client_setup | bytes (base64) | Protocol-specific public material used to construct challenges and verify proofs. |
| protocol | string | One of sw-priv, sw-pub, ateniese, erway, bjo. |
| audit_count | int | Number of successful proof rounds completed with this key. |
| blocks_audited | int | Cumulative number of blocks sampled across all proof rounds. |
Supported protocols:
| Protocol | Type | Proof Size | Challenges | Public Verify |
|---|---|---|---|---|
| sw-priv | POR | O(S) field elements | Unlimited | No |
| sw-pub | POR | O(S) group elements | Unlimited | Yes |
| ateniese | PDP | O(1) | Unlimited | No |
| erway | PDP+Updates | O(log n) | Unlimited | Yes |
| bjo | POR | O(1) per round | Bounded (Q) | No |
A tagis a small cryptographic authentication value computed from a block’s content and identity. When you call POST /tag, the service walks the full IPFS DAG of the given root CID and computes one tag per block. Tags are stored server-side. You receive the ordered list of block CIDs (block_ids) which you use to construct challenges client-side.
Multiple roots can be tagged under one key. The prover merges all tagged roots into a single proof when challenged with an empty roots array.
Proof of storage for a set of pinned CIDs follows five phases. Steps 1–2 are setup (done once). Steps 3–5 repeat each audit round.
Create a key
Choose a protocol and challenge size. Store the returned client_setup and key_id.
POST /prover/api/v1/challenge-key
{"protocol": "sw-priv", "challenge_size": 20}
→ {"key_id": "…uuid…", "client_setup": "…base64…"}Tag each pinned root
Call once per root CID. Store the returned block_ids for that root.
POST /prover/api/v1/tag
{"root": "bafybeig…", "key_id": "…uuid…"}
→ {"block_ids": ["…", "…", …]}Generate a challenge (client-side)
Use the client library with client_setup and block_ids to produce opaque challenge bytes and a local validator. No network call.
Submit the challenge (no auth required)
Pass key_id, optionally a list of root CIDs (empty = all tagged roots), and the challenge bytes. Receive raw proof bytes.
POST /prover/prove
{"key_id": "…uuid…", "roots": [], "challenge": "…base64…"}
→ <binary proof bytes>Verify the proof (client-side)
Call validator.Verify(challenge, proof) using the local validator from step 3. true = data intact. false = tampering or data loss detected. No network call.
| Method | Path | Body / Params | Response |
|---|---|---|---|
| POST | /prover/api/v1/challenge-key | {"protocol": "sw-priv", "challenge_size": 20} | {key_id, client_setup} |
| GET | /prover/api/v1/challenge-keys | — | Array of ChallengeKeyInfo |
| DELETE | /prover/api/v1/challenge-key/:id | — | 204 No Content |
| Method | Path | Description | Response |
|---|---|---|---|
| POST | /prover/api/v1/tag | Tag a pinned root CID under a key | {block_ids: [...]} |
| GET | /prover/api/v1/setup | Fetch client_setup + all block_ids for a key (?key_id=) | {client_setup, roots:[...]} |
| POST | /prover/api/v1/register | Client-side tag upload (Ateniese flow — caller computes tags) | 204 No Content |
| DELETE | /prover/api/v1/register/:key_id/:root | Remove tags for one root CID | 204 No Content |
| Method | Path | Body | Response |
|---|---|---|---|
| POST | /prover/prove | {key_id, roots, challenge} | Raw proof bytes (application/octet-stream) |
curl -X POST https://<env>.pinion.build/prover/prove \
-H "Content-Type: application/json" \
-d '{"key_id":"<uuid>","roots":[],"challenge":"<base64>"}' \
--output proof.bin