
AIOZ Pin API keys aren't one flat secret with full access by default, they're generated with a specific set of scopes attached, and the actual authentication mechanics differ slightly depending on which endpoint you're calling. This covers the parts that don't show up in a quick glance at the docs: which endpoints expect a Bearer token versus raw key/secret headers, exactly what a scoped key can and can't do, and how to store credentials safely once you have them.
TL;DR:
Authorization: Bearer <access_token>testAuthentication and the Nft API instead use raw pinning_api_key/pinning_secret_key headersadmin, data, pinning, pin_nft) rather than defaulting to full accessGenerating, listing, and deleting API keys, along with account endpoints like /users/me, all authenticate with the standard pattern: Authorization: Bearer your_access_token_here. But testAuthentication and the Nft API work differently, they expect pinning_api_key and pinning_secret_key as separate headers, the raw key and secret rather than a bearer token. That's not an inconsistency to route around, it lines up with what those endpoints actually do: testAuthentication exists specifically to verify a key/secret pair is valid, and the Nft API is built to be called directly with a scoped key rather than through an intermediate token exchange.
In practice, this distinction matters most when you're debugging: if a call to /users/me or /apiKeys/list fails with an auth error, check your Bearer token. If a call to testAuthentication or the Nft API fails, check the raw key and secret values themselves, those endpoints aren't reading a token at all.
Generating a key is a POST to /apiKeys with a scopes object:
{
"admin": false,
"data": { "pin_list": true, "nft_list": false },
"pinning": { "unpin": false, "pin_by_hash": true, "pin_file_to_ipfs": true },
"pin_nft": { "unpin_nft": false, "pin_nft_to_ipfs": false }
}
AIOZ Pin's docs specify that non-admin keys need every property included explicitly, there's no partial-scope shorthand. Admin keys are the one exception: set "admin": true and the sub-properties can be omitted entirely, since an admin key already has full access regardless of what the other fields say. This means a key handed to a build pipeline that only pins new files can be scoped to pinning.pin_file_to_ipfs alone, with no ability to unpin existing content, touch NFT operations, or read billing data, a meaningfully smaller blast radius than one shared admin key used everywhere.
Every AIOZ Pin surface, the REST API, the Node.js SDK, and the CLI, ultimately needs the same key and secret, just supplied differently. The SDK takes them directly in its constructor (new AiozPinClient(apiKey, secretKey)), the CLI accepts them as --key/--secret flags or reads them from a ~/.pinning/credentials JSON file so you don't retype them every invocation, and the raw REST API expects them wrapped into the appropriate header for whichever endpoint you're calling. Whichever layer you're using, avoid hardcoding the raw values into source code that ends up committed anywhere, environment variables or the CLI's credentials file keep the secret out of your codebase's history.
The practical reason scoping is worth setting up correctly, not just leaving every key at admin by default, shows up the moment credentials leak, and they eventually do: committed to a public repo by accident, exposed in a client-side bundle, pasted into the wrong Slack channel. An admin key leaked this way hands whoever finds it full account access: read billing, unpin anything, mint NFT records, everything. A key scoped to pinning.pin_file_to_ipfs alone, the kind a build pipeline actually needs, leaked the same way, only lets an attacker pin new files under your account, real damage in the sense of unwanted storage costs, but not the ability to delete existing pins, touch NFT operations, or read anything, because those permissions were never granted to that key in the first place. Scoping doesn't prevent a leak, nothing about key management does. It bounds what a leak actually costs, which is the entire point of generating separate keys per integration instead of one credential reused everywhere.
DELETE /api/apiKeys/:id removes a key immediately, using the id returned when you generated it or from a GET /api/apiKeys/list call. Because keys are scoped individually, rotating credentials for one integration doesn't require touching any other key you've issued, generate a new scoped key for the specific use case, update that one integration, then delete the old key once you've confirmed the new one works.
Before deciding what to rotate or tighten, it helps to actually see what's currently issued: GET /api/apiKeys/list returns every key on the account, its id, a truncated or masked view of the key itself, its scopes, and when it was created. For an account that's accumulated keys over time, one per integration as this article recommends, this is the practical starting point for a periodic security review: confirm every listed key still corresponds to something actually in use, and that none of them are scoped more broadly than the integration behind them actually needs. A key issued months ago for a project that's since been decommissioned, still valid, still capable of pinning or reading data, is exactly the kind of unnecessary exposure a five-minute audit against this endpoint catches before it becomes a real problem.
Does every AIOZ Pin API endpoint use the same authentication header? No. Account and key-management endpoints use Authorization: Bearer <access_token>, while testAuthentication and the Nft API use raw pinning_api_key and pinning_secret_key headers instead.
Can an AIOZ Pin API key be limited to only pinning, with no delete access? Yes. Scopes are granular down to individual permissions like pin_file_to_ipfs or unpin, so a key can be issued with only the specific permissions an integration actually needs.
Do I need to specify every scope property when generating a key? For non-admin keys, yes, AIOZ Pin's docs specify all properties must be included. Admin keys are the exception: setting admin: true alone is sufficient, sub-properties can be omitted.
How do I revoke an AIOZ Pin API key? DELETE /api/apiKeys/:id, using the key's ID from when it was generated or from a GET /api/apiKeys/list call.
Where should I store my AIOZ Pin API key and secret? Not in source code. Use environment variables for the SDK or REST API, or the CLI's ~/.pinning/credentials file, so the raw secret doesn't end up committed to a repository.
~/.pinning/credentials file storage pattern in practice
IPFS defaults to 256 KiB fixed-size chunks, but also ships Rabin and Buzhash content-defined chunkers. Here is why the choice affects deduplication.

An IPFS CID is not a random string. It encodes a version, a codec, and a hash algorithm plus digest. Here is how to decode a real CID piece by piece.

IPFS does not use a B-tree for large directories. It uses a HAMT, a Hash Array Mapped Trie. Here is exactly how it shards a folder once it outgrows one block.

AIOZ Pin image resizing happens straight in the gateway URL, no upload step or separate service. Here is every img- parameter, with real srcset examples.

x402 payments let ERC-8004 weight a paid AI agent interaction more heavily than free work in its reputation score. Here is exactly how that link works.

AIOZ Pin NFT API calls pin an asset and its metadata as two tracked pins under one record. Here is how to automate it directly over REST, no SDK needed.