SDKs
Official AppForges TypeScript, Python and Go SDKs, plus the OpenAPI specification.
TypeScript SDK
@appforges/sdk@v0.59.4The official SDK, generated automatically from the OpenAPI spec by the sdk-gen pipeline: zero runtime dependencies, dual ESM/CJS formats, typed errors, cursor-pagination iterators and SSE streaming out of the box. Response fields are wire-faithful (snake_case) — payloads pass through untouched.
Install
npm i @appforges/sdk
# or
bun add @appforges/sdkQuick start
import { AppForges } from "@appforges/sdk";
import { BearerAuth } from "@appforges/sdk/runtime/auth/bearer";
const client = new AppForges({
auth: new BearerAuth(process.env.APPFORGES_ACCESS_TOKEN!),
});
// Response fields are wire-faithful (snake_case) — payloads pass through.
const profile = await client.users.getProfile();
console.log(profile.user_id, profile.phone_number);Paginating
// Cursor-paginated list methods return a Paginator:
for await (const page of client.pinpagePages.listPages(profile.user_id, { query: { pageSize: 50 } })) {
// ...one item at a time
}
// or collect everything:
const all = await client.pinpagePages.listPages(profile.user_id).all();Error handling
import { AppForgesError } from "@appforges/sdk/runtime/errors";
try {
await client.users.getProfile();
} catch (error) {
if (error instanceof AppForgesError) {
console.error(error.httpStatus, error.requestId, error.retryAfterMs);
}
}AppForgesError carries httpStatus, requestId and retryAfterMs. Use reason and traceId to investigate requests.
Python SDK
appforges@0.59.4Python 3.11+ with synchronous and asynchronous clients, strict pydantic models, OAuth, typed errors, cursor iteration and SSE. Wire fields retain snake_case. Runtime dependencies are httpx and pydantic v2.
Install
pip install appforges==0.59.4Synchronous client
import os
from appforges import AppForges, BearerAuth
with AppForges(auth=BearerAuth(os.environ["APPFORGES_ACCESS_TOKEN"])) as client:
profile = client.users.get_profile()
print(profile.user_id)Asynchronous client
import asyncio
import os
from appforges import AsyncAppForges, BearerAuth
async def main() -> None:
async with AsyncAppForges(auth=BearerAuth(os.environ["APPFORGES_ACCESS_TOKEN"])) as client:
profile = await client.users.get_profile()
print(profile.user_id)
asyncio.run(main())The profile endpoint requires an OAuth access token representing a user. For staging, set base_url="https://api-staging.appforges.com". The with / async with contexts close client-owned connections.
SSE streaming
For APIs that declare SSE, iterate over events with for or async for. The current public specification does not declare an SSE endpoint, so no endpoint example is available yet.
Cursor iteration
with AppForges(auth=BearerAuth(os.environ["APPFORGES_ACCESS_TOKEN"])) as client:
profile = client.users.get_profile()
for page in client.pinpage_pages.list_pages_all(profile.user_id, page_size=50, max_pages=10):
print(page.page_id)
# AsyncAppForges exposes the same iterator through async for.Error handling
from appforges import APIError, RateLimitExceededError
with AppForges(auth=BearerAuth(os.environ["APPFORGES_ACCESS_TOKEN"])) as client:
try:
client.users.get_profile()
except RateLimitExceededError as error:
print(error.retry_after) # seconds
except APIError as error:
print(error.http_status, error.reason, error.request_id, error.trace_id)At most two retries by default, honoring Retry-After. Writes retry only under an idempotency contract. APIError retains status, reason, request_id and trace_id. Omitted fields are not sent; explicit None is accepted only for nullable fields.
Go SDK
appforges-sdk-go@v0.59.4Go 1.23+ with the standard library and no third-party runtime dependencies. Context cancellation, strict models, OAuth, typed errors, cursor pagination, and SSE; JSON fields retain snake_case names.
Install
go get github.com/appforges/appforges-sdk-go@v0.59.4Quick start
package main
import (
"context"
"fmt"
"os"
"time"
appforges "github.com/appforges/appforges-sdk-go"
"github.com/appforges/appforges-sdk-go/runtime"
)
func main() {
client, err := appforges.NewClient(runtime.WithBearerToken(os.Getenv("APPFORGES_ACCESS_TOKEN")))
if err != nil { panic(err) }
defer client.Close()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
profile, err := client.Users.GetProfile(ctx)
if err != nil { panic(err) }
fmt.Println(runtime.Value(profile.UserId))
}The profile API needs an OAuth access token representing a user. For staging, add runtime.WithBaseURL("https://api-staging.appforges.com"). Close releases idle connections owned by the client.
Pagination
for page, err := range client.PinpagePages.ListPagesAll(ctx, appforges.PinpagePagesListPagesParams{
UserId: runtime.Value(profile.UserId),
PageSize: runtime.Some[int64](50),
}) {
if err != nil { return err }
fmt.Println(runtime.Value(page.PageId))
// break stops fetching further pages.
}Error handling
import "errors"
var limited *appforges.RateLimitExceededError
var apiError *appforges.APIError
if errors.As(err, &limited) {
fmt.Println(limited.RetryAfter)
} else if errors.As(err, &apiError) {
fmt.Println(apiError.StatusCode, apiError.Reason, apiError.RequestID, apiError.TraceID)
}Up to two retries by default, honoring Retry-After. Writes follow the idempotency contract. errors.As exposes status, reason, request_id, and trace_id. Optional[T] with Some / Null distinguishes absence, values, and explicit null on nullable fields.
SSE streaming
APIs declaring SSE return iter.Seq2; break or context cancellation closes the response connection. The current public specification has no SSE endpoints, so there is no live endpoint example.
File data and Webhook verification
encodeUploadData / encode_upload_data / EncodeUploadData encode binary files smaller than 4 MiB for the public UploadAsset JSON base64 field. Helpers send no requests. The pre-signed upload flow is not currently public.
TypeScript
import { encodeUploadData } from "@appforges/sdk/runtime/upload";
import { verifyWebhookSignature } from "@appforges/sdk/runtime/webhook";
const data = encodeUploadData(new Uint8Array([0, 255]));
// Pass data to the generated UploadAsset request body.
async function verifyRequest(request: Request, originalSecret: string) {
const body = new Uint8Array(await request.arrayBuffer());
const signature = request.headers.get("X-Webhook-Signature") ?? "";
return verifyWebhookSignature(body, signature, [originalSecret]);
}Python
from appforges import encode_upload_data, verify_webhook_signature
data = encode_upload_data(bytes([0, 255]))
def verify_request(raw_body: bytes, signature: str, original_secret: str) -> bool:
return verify_webhook_signature(raw_body, signature, [original_secret])Go
import "github.com/appforges/appforges-sdk-go/runtime"
func encodeFile(data []byte) (string, error) {
return runtime.EncodeUploadData(data)
}
func verifyRequest(rawBody []byte, signature, originalSecret string) bool {
return runtime.VerifyWebhookSignature(rawBody, signature, []string{originalSecret})
}Verify X-Webhook-Signature using raw body bytes before JSON parsing and the original secret returned by create or rotate. Multiple local secrets support rotation transitions. The current protocol does not prevent replay or authenticate X-Webhook-Event.
SDK releases include API compatibility checks and an automatically generated CHANGELOG. Model, field and method documentation follows the OpenAPI specification.
OpenAPI specification
Prefer to generate your own client? Download the OpenAPI 3.1 spec and feed it into your favorite generator.
Current version v0.59.4, regenerated on every platform release.
Versioning
The wire schema your client speaks is controlled by the API-Version Versioning and upgrades header — see the dedicated guide.