@spade-dev/social-api
v0.1.0
Published
TypeScript SDK for the Spade social media API
Maintainers
Readme
@spade-dev/social-api
TypeScript SDK for the Spade social media API. Schedule posts, cross-post to 13 platforms, pull analytics, manage contacts, and wire up webhooks — all with full type safety.
Install
npm install @spade-dev/social-api
# or
pnpm add @spade-dev/social-apiQuickstart
import { SocialApiClient } from '@spade-dev/social-api'
const client = new SocialApiClient({ apiKey: 'sk_...' })
// Or set SOCIAL_API_KEY in your environment and call new SocialApiClient()Authentication
Pass your key directly or export it as an environment variable:
export SOCIAL_API_KEY=sk_your64hexkeyconst client = new SocialApiClient() // reads SOCIAL_API_KEY
const client = new SocialApiClient({ apiKey: 'sk_...' }) // explicitKeys follow the format sk_ + 64 lowercase hex characters. Create them in the Spade dashboard.
Self-hosted / local override
const client = new SocialApiClient({
apiKey: 'sk_...',
baseUrl: 'http://localhost:3000/v1',
})Error handling
All errors throw a SpadeError with typed code, message, and status fields:
import { SocialApiClient, SpadeError } from '@spade-dev/social-api'
try {
await client.posts.get('nonexistent-id')
} catch (err) {
if (err instanceof SpadeError) {
console.error(err.code) // 'NOT_FOUND'
console.error(err.status) // 404
console.error(err.message) // 'Post not found'
}
}Error codes: UNAUTHORIZED · FORBIDDEN · NOT_FOUND · VALIDATION_ERROR · BAD_REQUEST · CONFLICT · RATE_LIMITED · PLATFORM_ERROR · OAUTH_ERROR · INTERNAL_ERROR
Examples
Schedule a post
const profile = await client.profiles.create({ name: 'Acme Brand' })
const post = await client.posts.create({
profileId: profile.id,
content: 'Big launch day! 🚀',
scheduledFor: '2026-07-04T14:00:00Z',
timezone: 'America/New_York',
platforms: [
{ platform: 'twitter', accountId: 'acc-twitter-uuid' },
{ platform: 'linkedin', accountId: 'acc-linkedin-uuid' },
{ platform: 'instagram', accountId: 'acc-ig-uuid' },
],
})
console.log(post.id, post.status) // 'scheduled'Cross-post with per-platform content
const post = await client.posts.create({
profileId: 'prof-uuid',
content: 'Default caption for all platforms.',
publishNow: true,
platforms: [
{
platform: 'linkedin',
accountId: 'acc-linkedin-uuid',
customContent: 'B2B angle: here is why this matters for your team.',
platformSpecificData: { orgId: 'li-org-id', visibility: 'PUBLIC' },
},
{
platform: 'tiktok',
accountId: 'acc-tiktok-uuid',
platformSpecificData: {
privacy_level: 'PUBLIC_TO_EVERYONE',
disable_duet: false,
disable_comment: false,
},
},
{
platform: 'youtube',
accountId: 'acc-yt-uuid',
platformSpecificData: {
title: 'My Video Title',
privacy: 'public',
category: '22',
},
},
],
})Upload media and attach to a post
const file = new Blob([fs.readFileSync('./photo.jpg')], { type: 'image/jpeg' })
const { mediaId } = await client.media.upload(file, 'photo.jpg')
const post = await client.posts.create({
profileId: 'prof-uuid',
content: 'Check out this photo!',
publishNow: true,
platforms: [{ platform: 'instagram', accountId: 'acc-ig-uuid' }],
mediaItems: [{ mediaId, position: 0 }],
})Get analytics
// Post-level snapshots
const snapshots = await client.analytics.getPosts({
accountId: 'acc-uuid',
platform: 'twitter',
from: '2026-06-01',
to: '2026-06-30',
})
// Daily time-series for charting
const daily = await client.analytics.getDaily({
accountId: 'acc-uuid',
from: '2026-06-01',
})
// Best time to post
const { recommendations } = await client.analytics.getBestTime({ accountId: 'acc-uuid' })
// recommendations[0] = { dayOfWeek: 3, hour: 14, avgEngagement: 42.5, sampleSize: 10 }
// dayOfWeek: 0=Sun, 6=Sat | hour: 0–23 UTCSet up webhooks
const webhook = await client.webhooks.create({
url: 'https://your-app.com/webhooks/spade',
events: ['post.published', 'post.failed', 'post.scheduled', 'account.connected'],
})
// webhook.secret is shown only once — store it securely
console.log(webhook.secret)Verify incoming webhook signatures:
import { createHmac, timingSafeEqual } from 'crypto'
function verifyWebhook(rawBody: string, secret: string, signature: string): boolean {
const expected = createHmac('sha256', secret).update(rawBody).digest('hex')
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}
// In your webhook handler:
const sig = req.headers['x-spade-signature']
const event = req.headers['x-spade-event']
if (!verifyWebhook(req.rawBody, process.env.WEBHOOK_SECRET!, sig)) {
return res.status(401).send('Invalid signature')
}Webhook event catalog:
| Event | When |
|---|---|
| post.published | A platform target published successfully |
| post.failed | A platform target failed |
| post.partial | Some targets published, some failed |
| post.scheduled | A post was queued for future delivery |
| account.connected | OAuth flow completed |
| account.disconnected | Account manually disconnected |
| webhook.test | Fired by webhooks.test() |
Manage contacts & broadcasts
// Import contacts
await client.contacts.bulkCreate([
{ name: 'Alice', email: '[email protected]', tags: ['vip'] },
{ name: 'Bob', phone: '+15551234567' },
])
// Create a broadcast DM campaign
const broadcast = await client.broadcasts.create({
name: 'Summer Sale',
message: 'Hey {name}, our summer sale is live — 30% off everything!',
accountId: 'acc-ig-uuid',
})
// Add recipients
const contacts = await client.contacts.list({ tag: 'vip' })
await client.broadcasts.addRecipients(
broadcast.id,
contacts.data.map(c => c.id),
)
// Send immediately or schedule
await client.broadcasts.schedule(broadcast.id, '2026-07-01T09:00:00Z')Automated DM sequences
const sequence = await client.sequences.create({
name: 'Welcome Series',
accountId: 'acc-ig-uuid',
steps: [
{ message: 'Hey, welcome! Thanks for following us.', delayMinutes: 0 },
{ message: "Here's a tip to get started...", delayMinutes: 1440 }, // 1 day
{ message: 'Any questions? Reply and we will help.', delayMinutes: 4320 }, // 3 days
],
})
await client.sequences.activate(sequence.id)
// Enroll new followers
await client.sequences.enroll(sequence.id, { contactIds: ['contact-uuid-1', 'contact-uuid-2'] })Comment automations (Instagram / Facebook)
const automation = await client.automations.create({
name: 'Price inquiry',
accountId: 'acc-ig-uuid',
keyword: 'price',
replyMessage: 'Hi! DM us for pricing details 🙌',
isActive: true,
})
// Check trigger logs
const logs = await client.automations.getLogs(automation.id, { triggered: true, limit: 20 })Paid ads
// List Meta campaigns
const campaigns = await client.ads.listCampaigns({ accountId: 'acc-fb-uuid' })
// Boost a published post
const boost = await client.ads.boost({
accountId: 'acc-fb-uuid',
postId: '1234567890_9876543210',
budget: 100,
durationDays: 7,
targeting: { age_min: 25, age_max: 45 },
})
// Pull campaign analytics
const stats = await client.ads.getAnalytics({
accountId: 'acc-fb-uuid',
campaignId: boost.id,
from: '2026-07-01',
to: '2026-07-07',
})Inbox (DMs, comments, reviews)
// List DM conversations
const conversations = await client.inbox.listConversations('acc-ig-uuid')
// Reply to a DM
await client.inbox.sendMessage(conversations[0]!.id, 'acc-ig-uuid', 'Thanks for reaching out!')
// Reply to a comment
const comments = await client.inbox.listComments('acc-ig-uuid')
await client.inbox.replyToComment(comments[0]!.postId, 'acc-ig-uuid', '❤️ Thanks!')
// Reply to a Google Business review
const reviews = await client.inbox.listReviews('acc-gbp-uuid')
await client.inbox.replyToReview(reviews[0]!.id, 'acc-gbp-uuid', 'Thank you for the kind words!')Resource reference
| client.X | Methods |
|---|---|
| profiles | list · get · create · update · delete |
| accounts | list · get · health · disconnect |
| connect | getAuthUrl · connectBluesky · connectTelegram |
| posts | create · list · get · update · delete · retry |
| analytics | getPosts · getDaily · getBestTime · getAccount · getInstagramDemographics · getYouTubeDailyViews |
| webhooks | list · create · update · delete · getLogs · test |
| media | requestUpload · getUploadStatus · upload (convenience) |
| inbox | listConversations · getMessages · sendMessage · listComments · getPostComments · replyToComment · listReviews · replyToReview |
| contacts | list · create · bulkCreate · get · update · delete · setField · deleteField · listChannels |
| broadcasts | list · create · get · update · delete · send · schedule · cancel · listRecipients · addRecipients |
| sequences | list · create · get · update · delete · activate · pause · enroll · unenroll · listEnrollments |
| automations | list · create · get · update · delete · getLogs |
| ads | listCampaigns · createCampaign · getCampaign · updateCampaign · deleteCampaign · listAdSets · createAdSet · listAds · createAd · boost · getAnalytics · searchInterests |
Supported platforms
instagram · facebook · twitter · linkedin · tiktok · youtube · pinterest · reddit · bluesky · threads · snapchat · telegram · google_business
License
MIT
