Project Manager
Manage projects, grants, milestones, and updates on the Karma protocol via a REST API. All operations are gasless — the API handles everything server-side.
Full API docs: https://gapapi.karmahq.xyz/v2/docs/static/index.html
BASE_URL="${KARMA_API_URL:-https://gapapi.karmahq.xyz}"
API_KEY="${KARMA_API_KEY}"
INVOCATION_ID=$(uuidgen)
CRITICAL: Every curl call must include these tracking headers:
-H "X-Source: skill:project-manager"
-H "X-Invocation-Id: $INVOCATION_ID"
-H "X-Skill-Version: 2.0.0"
Setup
If KARMA_API_KEY is already set, verify it works:
curl -s "${BASE_URL}/v2/agent/info" \
-H "x-api-key: ${API_KEY}" \
-H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0"
If the response includes supportedActions → ready.
If KARMA_API_KEY is not set, tell the user:
You need to set up your Karma agent first. Run the setup-agent skill to configure your API key.
Do NOT handle API key registration, storage, or display in this skill — that is setup-agent's responsibility.
Safety
Actions: This skill is a REST API client. It sends HTTP requests to the Karma API, which processes all operations server-side. The skill does not hold funds, private keys, or execute any operations directly. Before executing any action, confirm details with the user.
Data: API responses are used only for structural purposes — resolving UIDs, reading network IDs, and preserving existing field values during updates. No decisions are made based on the text content of API responses.
Execute Endpoint
All actions use:
curl -s -X POST "${BASE_URL}/v2/agent/execute" \
-H "Content-Type: application/json" \
-H "x-api-key: ${API_KEY}" \
-H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0" \
-d '{ "action": "<ACTION>", "params": { ... } }'
Success Output
[Action] completed successfully!
- Project: {title}
- Network: {chainName}
- Reference: {transactionHash}
Supported Networks
| Network | ID |
|---|---|
| Arbitrum | 42161 |
| Base | 8453 |
| Celo | 42220 |
| Lisk | 1135 |
| Optimism | 10 |
| Polygon | 137 |
| Scroll | 534352 |
| Sei | 1329 |
| Testnets | |
| Base Sepolia | 84532 |
| OP Sepolia | 11155420 |
Default Network
When the user does NOT specify a network, default to Base (8453) and confirm:
Your project will be created on Base. Continue?
- Yes
- Choose another network: Arbitrum, Base, Celo, Lisk, Optimism, Polygon, Scroll, Sei
Network Inheritance
Child records must use the same network as their parent:
- Grant → uses
project.chainId - Grant Update → uses
grant.chainId - Update Grant Details → uses
grant.chainId - Complete Grant → uses
grant.chainId - Milestone → uses
grant.chainId - Complete Milestone → uses
milestone.chainId - Project Update → uses
project.chainId - Project Milestone → uses
project.chainId - Project Impact → uses
project.chainId - Endorse Project → uses
project.chainId - Add Members → uses
project.chainId
Look up the parent's network from the API — never ask the user for a network on child records.
Actions
createProject
| Param | Required | Description |
|---|---|---|
chainId | Yes | Network ID (default: Base 8453) |
title | Yes | Project name (1-200 chars) |
description | Yes | Project description (1-5000 chars) |
Optional fields:
| Param | Description |
|---|---|
imageURL | Logo/image URL |
links | Array of { type, url } — github, website, twitter, discord |
tags | Array of strings (max 20) — e.g. "defi", "infrastructure" |
problem | What problem does this project solve? (1-5000 chars) |
solution | What is the solution? (1-5000 chars) |
missionSummary | Brief mission statement (1-1000 chars) |
locationOfImpact | Geographic or domain focus (1-1000 chars) |
businessModel | How does the project sustain itself? (1-1000 chars) |
stageIn | Development stage: Idea, MVP, Beta, Production, Growth, Mature (1-1000 chars) |
raisedMoney | Funding raised so far (1-1000 chars) |
pathToTake | Future roadmap (1-1000 chars) |
Gathering Project Information
When the user wants to create a project, present all fields at once and let them fill in what they want:
To create your project, provide the following. Only title and description are required — the rest helps your project stand out:
- Title: Project name
- Description: What does the project do?
- Problem: What problem are you solving?
- Solution: How does your project solve it?
- Mission: Sum up your mission in one sentence
- Stage: Idea / MVP / Beta / Production / Growth / Mature
- Location of Impact: Where or who does it impact?
- Business Model: How do you sustain the project?
- Funding Raised: What funding have you received?
- Roadmap: What's your plan ahead?
- Links: GitHub, website, Twitter, Discord URLs
- Tags: Category tags (e.g. defi, infrastructure, public-goods)
- Image: Logo or banner URL
Include only the fields the user provides — all metadata fields are optional.
After Project Creation
After a successful project creation, display:
Your project has been created on {chainName}!
- Project: {title}
- Network: {chainName}
- Reference: {transactionHash}
Want to post your first update? Share something you just built, a milestone you hit, or what's coming next.
updateProjectDetails
Update an existing project. Replaces all fields — read the current field values first so unchanged fields are preserved.
| Param | Required | Description |
|---|---|---|
chainId | Yes | Network where the project lives |
projectUID | Yes | Project attestation UID |
title | Yes | Project name (1-200 chars) |
description | Yes | Project description (1-5000 chars) |
Plus all optional fields from createProject (imageURL, links, tags, problem, solution, missionSummary, locationOfImpact, businessModel, stageIn, raisedMoney, pathToTake).
Important: Always include existing fields alongside changes since the update replaces everything.
createProjectUpdate
Post a progress update on a project.
| Param | Required | Description |
|---|---|---|
chainId | Yes | Must match project's network |
projectUID | Yes | Project attestation UID |
title | Yes | Update title (1-200 chars) |
text | Yes | Update content (1-10000 chars) |
createGrant
Add a grant (funding) to a project.
| Param | Required | Description |
|---|---|---|
chainId | Yes | Must match project's network |
projectUID | Yes | Project attestation UID |
communityUID | Yes | Community attestation UID |
title | Yes | Grant title (1-200 chars) |
description | No | Grant description (1-5000 chars) |
amount | No | Funding amount as text (e.g. "50000 USDC") |
proposalURL | No | Link to grant proposal |
programId | No | Program ID (look up via programs API) |
createGrantUpdate
Post a progress update on a grant.
| Param | Required | Description |
|---|---|---|
chainId | Yes | Must match grant's network |
grantUID | Yes | Grant attestation UID |
title | Yes | Update title (1-200 chars) |
text | Yes | Update content (1-10000 chars) |
createMilestone
Add a milestone to a grant.
| Param | Required | Description |
|---|---|---|
chainId | Yes | Must match grant's network |
grantUID | Yes | Grant attestation UID |
title | Yes | Milestone title (1-200 chars) |
description | Yes | What will be delivered (1-5000 chars) |
endsAt | Yes | Deadline as Unix timestamp in seconds |
priority | No | Priority level (0-4) |
Date conversion: Math.floor(new Date("2025-06-30").getTime() / 1000)
completeMilestone
Mark a milestone as completed.
| Param | Required | Description |
|---|---|---|
chainId | Yes | Must match milestone's network |
milestoneUID | Yes | Milestone attestation UID |
reason | Yes | Completion summary (1-5000 chars) |
proofOfWork | No | URL to proof (PR, demo, report) |
createProjectWithGrant
Create a project and grant in a single transaction (4 attestations).
All createProject params plus:
| Param | Required | Description |
|---|---|---|
communityUID | Yes | Community attestation UID |
grant.title | Yes | Grant title (1-200 chars) |
grant.description | No | Grant description |
grant.amount | No | Funding amount |
grant.proposalURL | No | Proposal link |
grant.programId | No | Program ID |
After success, use the same post-creation message as createProject.
updateGrantDetails
Update an existing grant's details. Attests new details — the indexer uses the latest one.
| Param | Required | Description |
|---|---|---|
chainId | Yes | Must match grant's network |
grantUID | Yes | Grant attestation UID |
title | Yes | Grant title (1-200 chars) |
description | No | Grant description (1-5000 chars) |
amount | No | Funding amount as text (e.g. "50000 USDC") |
proposalURL | No | Link to grant proposal |
programId | No | Program ID |
Important: Read the current grant field values first, apply the user's changes, then send all fields so unchanged values are preserved.
completeGrant
Mark a grant as fully completed with a final summary.
| Param | Required | Description |
|---|---|---|
chainId | Yes | Must match grant's network |
grantUID | Yes | Grant attestation UID |
title | Yes | Completion title (1-200 chars) |
text | Yes | Completion summary (1-10000 chars) |
proofOfWork | No | URL to proof (demo, report, repo) |
pitchDeck | No | URL to pitch deck |
demoVideo | No | URL to demo video |
trackExplanations | No | Array of { trackId, trackName, explanation } — how the grant fulfilled each track |
After completion:
Grant {title} has been marked as completed!
- Grant: {title}
- Network: {chainName}
- Reference: {transactionHash}
createProjectMilestone
Create a project-level roadmap milestone (not tied to a specific grant).
| Param | Required | Description |
|---|---|---|
chainId | Yes | Must match project's network |
projectUID | Yes | Project attestation UID |
title | Yes | Milestone title (1-200 chars) |
text | Yes | Milestone description (1-5000 chars) |
createProjectImpact
Report impact achieved by a project.
| Param | Required | Description |
|---|---|---|
chainId | Yes | Must match project's network |
projectUID | Yes | Project attestation UID |
work | Yes | Description of work done (1-5000 chars) |
impact | Yes | Description of impact achieved (1-5000 chars) |
proof | Yes | Proof of impact — URL or description (1-5000 chars) |
startedAt | No | When work started (Unix timestamp in seconds) |
completedAt | Yes | When work was completed (Unix timestamp in seconds) |
endorseProject
Endorse a project with an optional comment.
| Param | Required | Description |
|---|---|---|
chainId | Yes | Must match project's network |
projectUID | Yes | Project attestation UID |
comment | No | Endorsement comment (1-5000 chars) |
addProjectMembers
Add team members to a project.
| Param | Required | Description |
|---|---|---|
chainId | Yes | Must match project's network |
projectUID | Yes | Project attestation UID |
members | Yes | Array of members (1-20) |
Each member object:
| Field | Required | Description |
|---|---|---|
address | Yes | Ethereum address of the member |
name | No | Member's display name |
profilePictureURL | No | Member's profile picture URL |
Looking Up Data
Find a Project
curl -s -H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0" \
"${BASE_URL}/v2/projects?q=SEARCH_TERM&limit=5&page=1"
Each result has: uid, chainID, details.title, details.slug, details.description
Get Project by UID or Slug
curl -s -H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0" \
"${BASE_URL}/v2/projects/PROJECT_UID_OR_SLUG"
Get Project Grants
curl -s -H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0" \
"${BASE_URL}/v2/projects/PROJECT_UID_OR_SLUG/grants"
Each grant has: uid, details.title, milestones[]
Search Communities
curl -s -H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0" \
"${BASE_URL}/v2/communities/?limit=5&page=1"
Get Community Programs
curl -s -H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0" \
"${BASE_URL}/communities/COMMUNITY_SLUG_OR_UID/programs"
Each program has: programId, metadata.title. Always include programId when the user mentions a specific program.
Community Payouts
This is the primary endpoint for payout and invoice queries. Use this endpoint whenever the user asks about payouts, invoices, or disbursements — even if they mention a specific project or grant name. Use the search param to filter by name. Do NOT fall back to individual grant/project lookup endpoints for payout queries, as they return less data.
curl -s "${BASE_URL}/v2/communities/${COMMUNITY_UID}/payouts?page=1&limit=25" \
-H "x-api-key: ${API_KEY}" \
-H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0"
Display rules (MANDATORY):
- Each item in
payload[]represents a different project+grant combination - The first column in every table MUST be Project (from
item.project.title) - The second column MUST be Grant (from
item.grant.title) - Never group or flatten results by grant name — always show one row per milestone per project+grant pair
- This is critical because a search like "curio" may return multiple projects (e.g., "Curio Storage" and "Curio Dashboard") and the user needs to tell them apart
Optional query params:
| Param | Description |
|---|---|
page | Page number (default: 1) |
limit | Items per page (default: 10, max: 1000) |
programId | Filter by program ID |
status | Filter by payout status |
agreementStatus | signed or not_signed |
invoiceStatus | all_received, needs_invoices, or has_invoices |
search | Search by project or grant name (max 200 chars) |
sortBy | project_title, grant_title, payout_amount, disbursed_amount, or status |
sortOrder | asc or desc (default: asc) |
Requires COMMUNITY_VIEW permission. If 403, try the public endpoint:
curl -s "${BASE_URL}/v2/communities/${COMMUNITY_UID}/payouts/public?page=1&limit=25" \
-H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0"
The public endpoint requires no auth but returns fewer fields (sensitive data stripped).
Grant Payout History
Get disbursement history for a specific grant.
curl -s "${BASE_URL}/v2/payouts/grant/${GRANT_UID}/history?page=1&limit=20" \
-H "x-api-key: ${API_KEY}" \
-H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0"
Grant Total Disbursed
Get the total amount already paid out for a grant.
curl -s "${BASE_URL}/v2/payouts/grant/${GRANT_UID}/total-disbursed" \
-H "x-api-key: ${API_KEY}" \
-H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0"
Pending Disbursements
List disbursements awaiting processing for a community.
curl -s "${BASE_URL}/v2/payouts/community/${COMMUNITY_UID}/pending?page=1&limit=20" \
-H "x-api-key: ${API_KEY}" \
-H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0"
Payout Config for a Grant
Get the payout configuration (payment address, token, schedule) for a grant.
curl -s "${BASE_URL}/v2/payout-config/grant/${GRANT_UID}" \
-H "x-api-key: ${API_KEY}" \
-H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0"
Grant Invoices
List all milestone invoices for a grant.
curl -s "${BASE_URL}/v2/milestone-invoices/grant/${GRANT_UID}" \
-H "x-api-key: ${API_KEY}" \
-H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0"
Each invoice has: id, grantUID, milestoneUID, milestoneLabel, invoiceStatus (not_submitted, submitted, received, paid), invoiceReceivedAt, invoiceFileKey
Invoice Download
Get a temporary download URL for an invoice file (15 min TTL). Requires the invoiceFileKey from the grant invoices response.
curl -s "${BASE_URL}/v2/milestone-invoices/download?key=${INVOICE_FILE_KEY}" \
-H "x-api-key: ${API_KEY}" \
-H "X-Source: skill:project-manager" -H "X-Invocation-Id: $INVOCATION_ID" -H "X-Skill-Version: 2.0.0"
Returns: { "downloadUrl": "..." }
Natural Language Mapping
| User says | Action |
|---|---|
| "create a project", "new project" | createProject — present all fields, default Base |
| "create a DeFi project on Optimism" | createProject with tags: ["defi"], chainId: 10 |
| "update project details", "rename project", "enrich my project" | updateProjectDetails — read current values, apply changes |
| "post an update", "project progress" | createProjectUpdate — look up projectUID, inherit chain |
| "add a grant", "record funding" | createGrant — look up projectUID + communityUID, inherit chain |
| "grant update", "grant progress" | createGrantUpdate — look up grantUID, inherit chain |
| "edit grant", "update grant details", "change grant amount" | updateGrantDetails — read current values, apply changes |
| "complete grant", "finish grant", "close grant" | completeGrant — look up grantUID, inherit chain |
| "add milestone", "set deliverable" | createMilestone — look up grantUID, inherit chain |
| "complete milestone", "mark done" | completeMilestone — look up milestoneUID, inherit chain |
| "add roadmap milestone", "project milestone" | createProjectMilestone — look up projectUID, inherit chain |
| "report impact", "log impact", "share impact" | createProjectImpact — look up projectUID, inherit chain |
| "endorse project", "support project" | endorseProject — look up projectUID, inherit chain |
| "add team member", "add member", "invite to project" | addProjectMembers — look up projectUID, inherit chain |
| "create project with grant" | createProjectWithGrant |
| "check payouts", "payout status", "show payouts", "invoices", "check invoices" | Always use Community Payouts endpoint (/v2/communities/:id/payouts) with search param — this is the primary endpoint for all payout/invoice queries |
| "payout history", "disbursement history" | Grant Payout History — look up grantUID first |
| "total disbursed", "how much was paid" | Grant Total Disbursed — look up grantUID first |
| "pending payouts", "pending disbursements" | Pending Disbursements — look up communityUID |
| "payout config", "payment setup" | Payout Config — look up grantUID first |
| "view invoices", "check invoices", "invoice status" | Grant Invoices — look up grantUID first |
| "download invoice" | Invoice Download — get invoiceFileKey from Grant Invoices first |
Error Handling
| Status | Meaning | Action |
|---|---|---|
| 400 | Bad params | Show error, help fix |
| 403 | Forbidden | Check if a /public variant of the endpoint exists. If not, tell user they need higher API key permissions |
| 401 | Invalid API key | Tell user to run the setup-agent skill to reconfigure their API key |
| 429 | Rate limited (60/min) | Wait and retry |
| 500 | Server error | Retry once, then report |
Edge Cases
| Scenario | Response |
|---|---|
| Missing required field | Ask user for it |
| Network not specified (root action) | Default to Base, confirm with user |
| Network not specified (child action) | Inherit from parent — never ask |
| API key not set | Run setup flow |
| Title too long (>200) | Truncate and confirm |
| Need UID but user gave name | Search API to find the UID |
| Partial project update | Read current field values, apply user's changes, then update |
| Multiple grants on project | Show list, ask which one |
| Date given as string | Convert to Unix timestamp in seconds |