Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

ProjectsClient

ProjectsClient is the main entry point for interacting with the Inkd Protocol. It handles x402 payments automatically.

import { ProjectsClient } from "@inkd/sdk"
 
const client = new ProjectsClient({
  privateKey: process.env.INKD_PRIVATE_KEY,
})

Projects

createProject

Register a new project on-chain. Costs $0.10+ USDC (paid automatically via x402).

const project = await client.createProject({
  name:          "my-agent",          // required, max 64 chars, globally unique
  description:   "My agent project",  // optional, max 1024 chars
  license:       "MIT",               // optional, SPDX identifier
  isPublic:      true,                // optional, default true
  isAgent:       true,                // optional, marks as AI agent
  agentEndpoint: "https://...",       // optional, agent HTTP endpoint
  readmeHash:    "ar://...",          // optional, Arweave README hash
})
 
console.log(project.id)     // "10"
console.log(project.txHash) // "0xabc..."
Returns:
{
  id:          string   // on-chain project ID
  name:        string
  owner:       string   // your wallet address
  txHash:      string
  blockNumber: string
}

Throws: NameTaken, NameTooLong, InsufficientFunds


getProject

const project = await client.getProject(10)
 
console.log(project.name)         // "my-agent"
console.log(project.owner)        // "0x..."
console.log(project.versionCount) // 2

Returns: Full project object from the registry.


listProjects

const { data, total } = await client.listProjects({
  offset: 0,
  limit:  20,
})
 
for (const project of data) {
  console.log(project.name, project.owner)
}

getOwnerProjects

const projects = await client.getOwnerProjects("0xYourAddress")

Versions

pushVersion

Upload content to Arweave and record it on-chain. Fee = Arweave cost + 20% markup (min $0.10 USDC).

// With local file — uploads to Arweave automatically
const version = await client.pushVersion({
  projectId:  10,
  file:       "./dist/agent.js",     // path or Buffer
  versionTag: "v1.0.0",
  changelog:  "Initial release",
})
 
// With existing Arweave hash
const version = await client.pushVersion({
  projectId:   10,
  arweaveHash: "ar://QmAbc123...",
  versionTag:  "v1.0.0",
  changelog:   "Initial release",
})
 
console.log(version.arweaveHash) // "ar://QmAbc123..."
console.log(version.arweaveUrl)  // "https://arweave.net/QmAbc123..."
console.log(version.txHash)      // "0xdef..."
Returns:
{
  projectId:    string
  versionIndex: string
  versionTag:   string
  arweaveHash:  string
  arweaveUrl:   string
  txHash:       string
  blockNumber:  string
}

Throws: ProjectNotFound, NotOwnerOrCollaborator, EmptyArweaveHash, EmptyVersionTag


getVersion

const version = await client.getVersion(10, 0) // projectId, versionIndex
 
console.log(version.versionTag)  // "v1.0.0"
console.log(version.arweaveHash) // "ar://QmAbc123..."
console.log(version.pushedBy)    // "0x..."
console.log(version.pushedAt)    // Unix timestamp

listVersions

const { data } = await client.listVersions(10)
 
for (const version of data) {
  console.log(`${version.versionTag} → ${version.arweaveHash}`)
}

estimateVersionCost

Get the exact USDC fee for pushing a version before paying:

const estimate = await client.estimateVersionCost({ bytes: 102400 })
 
console.log(estimate.totalUsd)  // "$0.0055"
console.log(estimate.total)     // "5520" (USDC, 6 decimals)

Collaborators

addCollaborator

Grant push access to another address. Owner only. Max 50 collaborators per project.

await client.addCollaborator({
  projectId:    10,
  collaborator: "0xCollaboratorAddress",
})

removeCollaborator

await client.removeCollaborator({
  projectId:    10,
  collaborator: "0xCollaboratorAddress",
})

isCollaborator

const isCollab = await client.isCollaborator(10, "0xAddress")
// true | false

Content

uploadContent

Upload a file to Arweave directly, without pushing a version. Returns an ar:// hash.

import { readFileSync } from "fs"
 
const result = await client.uploadContent({
  data:        readFileSync("./dist/agent.js"),
  contentType: "application/javascript",
  filename:    "agent.js",         // optional
})
 
console.log(result.hash) // "ar://QmAbc123..."
console.log(result.url)  // "https://arweave.net/QmAbc123..."

Full agent example

import { ProjectsClient } from "@inkd/sdk"
import { readFileSync } from "fs"
 
const client = new ProjectsClient({
  privateKey: process.env.INKD_PRIVATE_KEY,
})
 
async function deploy() {
  // Create project (idempotent in practice — catch NameTaken)
  let projectId: string
 
  try {
    const project = await client.createProject({
      name:    "my-agent",
      license: "MIT",
      isAgent: true,
    })
    projectId = project.id
    console.log(`Created project #${projectId}`)
  } catch (err: any) {
    if (err.code === "NameTaken") {
      const existing = await client.getProject("my-agent")
      projectId = existing.id
    } else throw err
  }
 
  // Push version
  const version = await client.pushVersion({
    projectId:  projectId,
    file:       "./dist/agent.js",
    versionTag: `v${process.env.npm_package_version}`,
    changelog:  process.env.CHANGELOG ?? "",
  })
 
  console.log(`Pushed ${version.versionTag} → ${version.arweaveHash}`)
}
 
deploy().catch(console.error)

Private Uploads

Store content end-to-end encrypted on Arweave. Only authorized wallets can decrypt.

// Upload encrypted — requires privateKey in config
const result = await client.pushPrivateVersion(projectId, {
  content:     fs.readFileSync("./model.bin"),
  tag:         "v1.0.0",
  contentType: "application/octet-stream",
});
 
console.log(result.isPrivate);    // true
console.log(result.contentHash);  // ar:// of encrypted blob
console.log(result.metadataHash); // ar:// of access manifest (wrapped keys)
 
// Decrypt
const plaintext = await client.decryptVersion(
  result.contentHash,
  result.metadataHash
);

See Private Uploads for the full security model.