SDK for interacting with the Crosspost Proxy Service.
This package is designed to be used with near-sign-verify for authenticating requests via a wallet or keypair.
bun install @crosspost/sdk
import * as near from "fastintear"; // or near-api-js for creating key pairs
import { sign } from "near-sign-verify";
import {
CrosspostClient,
// error handling helpers
CrosspostError,
isAuthError,
isPlatformError,
isRateLimitError,
isValidationError
} from '@crosspost/sdk';
import type {
ConnectedAccount,
CreatePostRequest,
} from "@crosspost/sdk";
// Initialize the client
const client = new CrosspostClient({
baseUrl: 'https://your-self-hosted-crosspost-api.com', // Optional: Defaults to official API
});
const authToken = await sign({ signer: near, recipient: "crosspost.near", message: "createPost" });
client.setAuthentication(authToken);
client.setAccountHeader("signer.near")
const connectedAccounts: ApiResponse<ConnectedAccountsResponse> = await client.auth.getConnectedAccounts():
try {
const response = await await client.post.createPost({
targets: [
{
userId: connectedAccounts[0].userId,
platform: connectedAccounts[0].platform
}
],
content: [{
text: "hello world",
media: {
data: imageBlob,
mimeType: 'image/jpeg',
altText: 'a beautiful sunset',
}
}]
} as CreatePostRequest);
console.log('Post created successfully');
console.log('Post ID:', response.id);
console.log('Platform:', response.platform);
console.log('URL:', response.url);
console.log('Created at:', response.createdAt);
} catch (error) {
// Check if it's an authentication error
if (isAuthError(error)) {
console.error('Authentication required. Attempting to authorize...');
// The account must be authorized with the backend
const authorized = await client.auth.authorizeNearAccount();
if (authorized) {
// Retry the operation
return createPost();
}
} else {
// Handle other error types
console.error('Error creating post:', error);
if (error instanceof CrosspostError) {
// Use error utility functions to handle specific cases
if (isPlatformError(error)) {
console.error('Platform:', error.platform);
console.error('Error code:', error.code);
console.error('Details:', error.details);
} else if (isRateLimitError(error)) {
console.error('Rate limited until:', error.details?.rateLimit?.reset);
} else if (isValidationError(error)) {
console.error('Validation errors:', error.details?.validationErrors);
}
// Check if error is recoverable
if (error.recoverable) {
console.log('This error is recoverable - retry may succeed');
}
} else if (error instanceof Error) {
// Handle non-API errors (network issues, etc)
console.error('Unexpected error:', error.message);
}
}
}
-
client.setAuthentication(authToken: string): Promise<void>
- Sets authentication data, necessary for non-GET requests -
client.isAuthenticated(): boolean
- Checks if client is authenticated -
client.setAccountHeader(accountId: string): Promise<void>
- Sets X-Near-Account Header, necessary for GET requests -
client.clear(): boolean
- Clears authentication and account header
-
client.auth.authorizeNearAccount(): Promise<ApiResponse<NearAuthorizationResponse>>
- Authorizes NEAR account -
client.auth.unauthorizeNearAccount(): Promise<ApiResponse<NearAuthorizationResponse>>
- Unauthorizes NEAR account -
client.auth.getNearAuthorizationStatus(): Promise<ApiResponse<NearAuthorizationResponse>>
- Checks authorization status for authenticated account -
client.auth.loginToPlatform(platform, options?): Promise<AuthCallbackResponse | ApiResponse<AuthUrlResponse>>
- Opens popup to initiate OAuth flow with platform -
client.auth.refreshToken(platform): Promise<ApiResponse<AuthCallbackResponse>>
- Refreshes platform token -
client.auth.refreshProfile(platform): Promise<ApiResponse<ConnectedAccount>>
- Refreshes user profile -
client.auth.getAuthStatus(platform): Promise<ApiResponse<AuthStatusResponse>>
- Gets authentication status -
client.auth.revokeAuth(platform): Promise<ApiResponse<AuthRevokeResponse>>
- Revokes platform access -
client.auth.getConnectedAccounts(): Promise<ApiResponse<ConnectedAccountsResponse>>
- Lists connected accounts
Each post operation accepts a request object that includes:
-
targets
: Array of{ platform: string, userId: string }
specifying where to perform the action - Additional parameters specific to each operation
Available methods:
-
client.post.createPost(request: CreatePostRequest): Promise<CreatePostResponse>
- Creates posts on specified platforms -
client.post.repost(request: RepostRequest): Promise<RepostResponse>
- Reposts an existing post -
client.post.quotePost(request: QuotePostRequest): Promise<QuotePostResponse>
- Quotes an existing post -
client.post.replyToPost(request: ReplyToPostRequest): Promise<ReplyToPostResponse>
- Replies to a post -
client.post.likePost(request: LikePostRequest): Promise<LikePostResponse>
- Likes a post -
client.post.unlikePost(request: UnlikePostRequest): Promise<UnlikePostResponse>
- Unlikes a post -
client.post.deletePost(request: DeletePostRequest): Promise<DeletePostResponse>
- Deletes posts
-
client.activity.getLeaderboard(options): Promise<LeaderboardResponse>
- Gets activity leaderboard -
client.activity.getAccountActivity(signerId, options): Promise<AccountActivityResponse>
- Gets account activity -
client.activity.getAccountPosts(signerId, options): Promise<AccountPostsResponse>
- Gets account posts
-
client.system.getRateLimits(): Promise<RateLimitsResponse>
- Gets all rate limits -
client.system.getEndpointRateLimit(endpoint): Promise<EndpointRateLimitResponse>
- Gets endpoint rate limit -
client.system.getHealthStatus(): Promise<HealthStatusResponse>
- Gets API health status
The SDK supports offset-based pagination for endpoints that return large collections:
// Get paginated results with specific limit and offset
const response = await client.activity.getLeaderboard({
limit: 10, // Number of items per page
offset: 20, // Skip the first 20 items
});
// Access pagination metadata
console.log(`Total items: ${response.meta.pagination?.total}`);
console.log(`Current page size: ${response.meta.pagination?.limit}`);
console.log(`Current offset: ${response.meta.pagination?.offset}`);
Post operations always return multi-status responses:
// Operation targeting multiple platforms
const response = await client.post.createPost({
targets: [
{ platform: 'twitter', userId: 'user1' },
{ platform: 'facebook', userId: 'user2' },
],
content: [{ text: 'hello world' }],
});
// Check multi-status summary
console.log(`Total operations: ${response.data.summary.total}`);
console.log(`Successful: ${response.data.summary.succeeded}`);
console.log(`Failed: ${response.data.summary.failed}`);
// Access successful results
response.data.results.forEach((result) => {
console.log(`Success on ${result.platform}: ${result.details.id}`);
});
// Access errors (if any)
if (response.data.errors && response.data.errors.length > 0) {
// Error structure is identical to error.details.errors when all operations fail
response.data.errors.forEach((error) => {
console.log(`Error on ${error.details.platform}: ${error.message}`);
console.log(`Error code: ${error.code}`);
console.log(`Recoverable: ${error.recoverable}`);
});
}
If all operations fail, the SDK throws a CrosspostError
with the same error structure in
details.errors
:
try {
await client.post.createPost({...});
} catch (error) {
if (error instanceof CrosspostError) {
// Error structure is identical to response.data.errors in partial success case
error.details.errors.forEach(err => {
console.log(`Error on ${err.details.platform}: ${err.message}`);
console.log(`Error code: ${err.code}`);
console.log(`Recoverable: ${err.recoverable}`);
});
}
}
This consistent error structure allows you to use the same error handling logic regardless of whether you're dealing with partial failures in a multi-status response or complete failure.
The SDK provides detailed validation error information:
try {
await client.post.createPost({
// Invalid or missing required fields
});
} catch (error) {
if (isValidationError(error)) {
console.error('Validation failed:');
// Access validation error details
if (error.details?.validationErrors) {
Object.entries(error.details.validationErrors).forEach(([field, issues]) => {
console.error(`Field '${field}': ${issues.join(', ')}`);
});
}
}
}
import {
getErrorDetails,
getErrorMessage,
isAuthError,
isContentError,
isMediaError,
isNetworkError,
isPlatformError,
isPostError,
isRateLimitError,
isRecoverableError,
isValidationError,
} from '@crosspost/sdk';
// Check error types
if (isAuthError(error)) {
// Handle authentication errors
}
// Get user-friendly error message
const message = getErrorMessage(error, 'Default message');
// Get error details
const details = getErrorDetails(error);
// Create a text post on Twitter
const textPostResponse = await client.post.createPost({
targets: [{
platform: 'twitter',
userId: 'your-twitter-id',
}],
content: [{
text: 'Hello from Crosspost SDK!',
}],
});
// Create a post with media on multiple platforms
const mediaPostResponse = await client.post.createPost({
targets: [
{ platform: 'twitter', userId: 'your-twitter-id' },
{ platform: 'facebook', userId: 'your-facebook-id' },
],
content: [{
text: 'Check out this image!',
media: [{
data: imageBlob,
mimeType: 'image/jpeg',
altText: 'A beautiful sunset',
}],
}],
});
// Like a post on Twitter
await client.post.likePost({
targets: [{
platform: 'twitter',
userId: 'your-twitter-id',
}],
platform: 'twitter',
postId: '1234567890',
});
// Repost on multiple platforms
await client.post.repost({
targets: [
{ platform: 'twitter', userId: 'your-twitter-id' },
{ platform: 'facebook', userId: 'your-facebook-id' },
],
platform: 'twitter',
postId: '1234567890',
});
// Reply to a post
await client.post.replyToPost({
targets: [{
platform: 'twitter',
userId: 'your-twitter-id',
}],
platform: 'twitter',
postId: '1234567890',
content: [{
text: 'This is a reply',
}],
});
// Delete posts
await client.post.deletePost({
targets: [{
platform: 'twitter',
userId: 'your-twitter-id',
}],
posts: [{
platform: 'twitter',
userId: 'your-twitter-id',
postId: '1234567890',
}],
});
// Get leaderboard
const leaderboard = await client.activity.getLeaderboard({
timeframe: 'week',
limit: 10,
});
// Get account activity
const activity = await client.activity.getAccountActivity('user.near', {
timeframe: 'month',
});
// Get account posts
const posts = await client.activity.getAccountPosts('user.near', {
limit: 20,
offset: 0,
});
// Get all rate limits
const rateLimits = await client.system.getRateLimits();
// Get rate limit for a specific endpoint
const postRateLimit = await client.system.getEndpointRateLimit('post');