@gibwork/sdk
v0.2.0
Published
TypeScript SDK for the Gibwork External API
Maintainers
Readme
Gibwork SDK
TypeScript SDK for the wallet-authenticated Gibwork External API.
It handles wallet addresses, nonces, timestamps, payload normalization, message signatures, HTTP requests, and Solana transaction signing for you.
Installation
The package is currently private while its npm name and license are finalized. Once published:
npm install @gibwork/sdkRequires Node.js 22 or newer.
Quick start
Load your environment variables in the application, then pass the private key
to the Node entry point. The SDK never loads .env implicitly.
SOLANA_PRIVATE_KEY=[1,2,3,...]import { createGibworkClient } from '@gibwork/sdk/node';
const gibwork = createGibworkClient({
privateKey: process.env.SOLANA_PRIVATE_KEY!,
production: true,
});Requests use the stage environment by default. Set production: true when
creating the client to route all of its requests to production. The setting is
client-wide, so prepare and transaction-submit requests always use the same
environment.
SOLANA_PRIVATE_KEY may be a base58-encoded 32-byte seed or 64-byte secret
key, or a JSON array containing 32 or 64 byte values.
Applications using a wallet adapter or custom custody provider can inject a signer instead:
import { GibworkClient, type WalletSigner } from '@gibwork/sdk';
declare const signer: WalletSigner;
const gibwork = new GibworkClient({
signer,
production: true,
});Methods
const task = await gibwork.tasks.create({
title: 'Write an SDK guide',
content: '<p>Document the Gibwork SDK.</p>',
tags: ['Docs'],
payment: {
mintAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
amount: '25.00',
},
minSubmissionAmount: '5.00',
});
const tasks = await gibwork.tasks.list();
const taskDetails = await gibwork.tasks.get(task.taskId);
console.log(taskDetails.content);
const updatedTask = await gibwork.tasks.update(task.taskId, {
content: '<p>Updated bounty instructions.</p>',
allowOnlyVerifiedSubmissions: true,
deadline: '2026-10-01T12:00:00.000Z',
});
const submissions = await gibwork.submissions.list(task.taskId);
await gibwork.submissions.approve(task.taskId, submissionId, {
amount: '5.00',
rating: 5,
});
await gibwork.submissions.reject(
task.taskId,
submissionId,
'Please include the source files.',
);
const comments = await gibwork.submissions.comments.list(
task.taskId,
submissionId,
);
await gibwork.submissions.comments.create(
task.taskId,
submissionId,
'Thanks for the update.',
);
await gibwork.tasks.refund(task.taskId);tasks.update changes metadata on an open bounty created by the signing wallet.
It can update content, allowOnlyVerifiedSubmissions, and deadline, and it
does not create or sign a Solana transaction. Deadline updates may be ignored by
the API for non-credit bounties worth less than $5 USD.
tasks.create, tasks.refund, and submissions.approve perform the complete
prepare, sign, and submit sequence. Advanced callers can control the phases
with:
tasks.prepareCreateandtasks.submitCreatetasks.prepareRefundandtasks.submitRefundsubmissions.prepareApprovalandsubmissions.submitApprovalsignPreparedTransaction
All request methods accept an optional final { signal } argument for
cancellation. Set the default request timeout with timeoutMs when creating
the client.
Discover tasks and submit work
const available = await gibwork.tasks.listAvailable({ page: 1, limit: 15 });
// Persist the key and exact input before sending. Supply your own UUIDv4.
const input = {
content: '<p>Completed work: https://example.com/work</p>',
idempotencyKey: savedIdempotencyKey,
};
const intent = await gibwork.submissions.create(taskId, input);
if (intent.status === 'submitted') {
const current = await gibwork.submissions.getIntent(taskId, intent.intentId);
}Discovery is free and wallet-authenticated across creators; tasks.list lists
created tasks, while tasks.get(taskId) returns one bounty's complete content,
reward, creator, media, and participation settings whether or not it appears on
the current discovery page. Discovery accepts only page and limit (defaults
1/15, maximum limit 100). Results show eligibility requirements, not a guarantee
that your wallet can submit. asset.amount and
minSubmissionAmount are strings in base units; the asset amount is the
original reward pool, not remaining funds.
Submitting work requires an active platform wallet, eligibility, and currently
0.15 USDC. Network costs are sponsored. Read the returned fee for amounts.
Optional referral and mediaIds are supported; media must already be uploaded
and owned by the caller. The SDK preserves submission content/referral exactly
when signing, including whitespace and HTML.
For separate phases use:
submissions.prepareCreate(taskId, input)signPreparedTransaction(prepared.serializedTransaction, signer)submissions.submitCreate(taskId, intentId, { paymentAttemptId, signedTransaction })submissions.getIntent(taskId, intentId)
Prepare can return an existing intent without a transaction. submissions.create
signs only a pending intent with a transaction and returns the state after one
submit; it does not poll or retry automatically.
| State | Meaning and recovery |
| -------------------- | -------------------------------------------------------------------------------------- |
| pending | Not submitted. Retry prepare with the original input/key to obtain the transaction. |
| submitted | Unresolved. Read the same intent later. |
| fulfilled | Fee confirmed and submission created. |
| failed / expired | Resolve the attempt before explicitly choosing a new key. |
| requires_review | Fee paid; operator attention required. Retain the intent ID/hash and do not pay again. |
Only fulfilled confirms creation. taskSubmissionId is allocated in advance;
its presence and HTTP 200 alone do not prove success. Intent status does not
report subsequent creator review/payout, and existing creator submission reads
have not been extended to participants.
After an interrupted prepare, reuse the exact input/key with the same wallet
and environment. After an uncertain submit, use getIntent with the original
wallet, including after wallet replacement. Each call generates fresh auth.
The same key still returns the original intent after failure/expiry. A 409 error
may contain an existing intent ID in its structured body.
The upstream wallet limits are discovery 30/minute, prepare 2/minute, submit
5/minute, and status 60/minute. Respect GibworkApiError.retryAfter (seconds or an
HTTP date) before another request; it is optional. The
stage example persists recovery data before
payment and uses explicit status reads on later invocations:
npm run build
node --env-file=.env examples/create-submission.mjs work.json recovery.jsonwork.json contains { "taskId": "UUID", "input": { "content": "...",
"idempotencyKey": "UUIDv4" } }. This example may charge the stage participation
fee. Once it records an intent, rerunning only reads status. No media upload or
operator resolution endpoint is provided by this SDK.
Errors
API failures throw GibworkApiError with status and the parsed response
body, plus optional requestId and raw retryAfter. Network failures and timeouts use typed SDK errors.
Transaction submits are never retried automatically. If the network or server
fails while submitting, the SDK throws GibworkAmbiguousSubmitError with safe
task, submission, and intent identifiers. Do not resubmit the same intent
blindly.
GibworkValidationError reports invalid discovery/submission input before a
request. GibworkProtocolError reports an unusable intent response. The
submissions.create convenience method wraps non-ambiguous failures in
GibworkSubmissionOperationError: context retains the phase, original
idempotency key, wallet/environment/API location, and intent identifiers when
known; cause retains the original typed error. Ambiguous payments remain
GibworkAmbiguousSubmitError with enriched context. A wallet rejection or
cancellation before dispatch is not classified as a broadcast payment.
Development
npm install
npm run checkThe package currently remains UNLICENSED; select and add the intended public
license before treating the release as generally reusable open-source software.
Private-key safety
The SDK never transmits the private key, loads .env implicitly, or includes
key material in its errors. Never expose the Node private-key entry point in a
browser bundle. Browser consumers should provide a wallet-adapter-backed
signer.
