Base URL
All endpoints live under /api/v1. Sandbox/staging URLs are provided at onboarding. Every request must set:
| Header | Value |
|---|---|
X-API-Key | sa_ + 64 hex chars (issued at activation) |
Content-Type | application/json |
Accept | application/json |
Example — curl
curl -s https://api.stridesaway.com/api/v1/creators \ -H "X-API-Key: sa_<your_key>"
Authentication
API requests are authenticated with your license key in the X-API-Key header (no Bearer prefix). Keys are issued once when your license activates and are shown only a single time — store them in a secrets manager.
| Status | Meaning |
|---|---|
| 200 | Authenticated & authorized |
| 401 | Missing, invalid, revoked, or expired key |
| 403 | Key is valid but the account is banned |
Your key is tied to one brand account. All resources created/read with the key belong to that account and are isolated from other customers.
Conventions
Money
All amounts are decimal major units (dollars, not cents). Examples: 100.00, 49.5. Internally they are integer cents.
Dates & times
ISO 8601 with timezone offset, e.g. 2026-12-01T18:00:00.000Z.
IDs
All resource IDs are UUIDs.
Errors
Errors return an application/json body with a single field:
{ "error": "a human-readable message" }| HTTP | When |
|---|---|
| 400 | Invalid input / validation failed |
| 401 | Bad or missing API key |
| 403 | Insufficient permission / banned account |
| 404 | Resource not found |
| 409 | Conflict (wrong state, e.g. already reviewed) |
| 500 | Internal error (retry; report if persistent) |
Pagination
List endpoints accept page (1-based) and per_page. The creators endpoint returns { "items": [...], "total": N, "page": P, "per_page": N }.
Workflow at a glance
- 1
Top up your wallet
POST /wallet/top-up → hosted checkout → poll status - 2
Create a brief
POST /briefs - 3
Fund the brief (wallet)
POST /briefs/:id/checkout → instant - 4
Creators apply
GET /briefs/:id/applicants - 5
Invite or select creators
POST /briefs/:id/invite | /select - 6
Creators submit
GET /briefs/:id/submissions - 7
Review drafts
POST /submissions/:id/review - 8
Confirm & pay
POST /submissions/:id/confirm
Prepaid wallet
Your API key is linked to a brand account with a prepaid wallet. Top it up via a hosted checkout, then funding briefs is an instant server-side debit. The balance is shared with the web dashboard.
All prepaid balances, one per currency. Currency conversion is handled by the payment provider at top-up; briefs are funded from their own-currency wallet.
Response 200
[
{ "id": "w1a2...", "brand_id": "b0f0...", "balance": 5000.0, "currency": "USD" },
{ "id": "w3b4...", "brand_id": "b0f0...", "balance": 250000.0, "currency": "NGN" }
]Start a top-up in a currency. Returns a hosted checkout URL (or completed: true when paid). Omit currency to use the platform currency ( USD).
Body
{ "amount": 500.0, "currency": "NGN", "return_url": "https://yourservice.com/wallet/return", "cancel_url": "https://yourservice.com/wallet/cancel" }Response 200
{ "completed": false, "currency": "NGN", "checkout_url": "https://pay.bachs.io/cs_..." }Poll the top-up. Once payment settles, the balance is credited and completed becomes true with the new balance_after (and the currency). This is also driven by webhooks, so polling is a fallback.
{ "status": "completed", "payment_status": "paid", "completed": true, "currency": "NGN", "balance_after": 250500.0 }Recent ledger entries (top-ups, brief-funding debits, refund credits), newest first.
Creators
Query params
| Param | Type | Description |
|---|---|---|
q | string | Free-text search |
niche | string | Filter by niche (e.g. health, tech) |
type | string | creator or clipper |
country | string | ISO country code |
page | int | Default 1 |
per_page | int | Default 20 |
Response 200
{
"items": [
{
"id": "3f0f...",
"display_name": "Maya Wellness",
"bio": "Fitness & lifestyle UGC",
"avatar_url": "https://...",
"country": "NG",
"state": "Lagos",
"niches": ["health", "wellness", "lifestyle"],
"types": ["creator"],
"social_links": { "instagram": "https://instagram.com/..." },
"follower_counts": { "instagram": 10000 },
"portfolio_links": ["https://..."],
"rate": 1500.0,
"rate_min": null,
"rate_max": null,
"currency": "USD"
}
],
"total": 1,
"page": 1,
"per_page": 20
}rate/rate_min/rate_maxare per-project indicative rates in decimal units.- Only non-banned, active creators appear.
Briefs
A brief is a package of deliverables (e.g. “10 product videos”). The total budget is the sum of all deliverable amounts. Two pricing models exist:
fixed— creators are paid a set amount per deliverable.cpm— clippers are paid per 1,000 verified views; you set an explicittotal_budgetpool instead of per-deliverable amounts.
Create a brief. It is created in draft status.
Body
{
"title": "Summer Fitness Campaign",
"description": "UGC videos for our activewear line",
"currency": "USD",
"deadline": "2026-12-31T23:59:59Z",
"required_niche": "health",
"selection_mode": "brand_selects",
"pricing_model": "fixed",
"requires_brand_approval": true,
"requires_shipping": false,
"deliverables": [
{
"title": "Gym lookbook video",
"description": "60s vertical video",
"amount": 100.00,
"due_date": "2026-11-30T18:00:00Z",
"scheduled_post_date": "2026-12-05T18:00:00Z"
}
]
}Fields
| Field | Type | Required | Notes |
|---|---|---|---|
title | string | yes | — |
description | string | no | — |
currency | string | no | ISO 4217, default USD |
deadline | datetime | no | Brief expires at deadline + grace_hours |
grace_hours | int | no | Grace after the deadline before expiry (default 24, 0 = none) |
required_niche | string | no | Matching creators are surfaced first |
selection_mode | string | no | brand_selects (default) or first_come |
pricing_model | string | no | fixed (default) or cpm |
requires_brand_approval | bool | no | Drafts need your review before posting |
requires_shipping | bool | no | Physical samples shipped to creators |
post_only | bool | no | Influencers post pre-made content (no creation) |
quantity | int | no | Auto-generate this many deliverables (alternative to deliverables[]) |
per_item_amount | number | no | Per-deliverable amount when using quantity |
brand_media_urls | string[] | post_only | Shared media copied to every quantity deliverable |
brand_caption | string | post_only | Shared caption/text for post-only briefs |
total_budget | number | for cpm | Explicit budget pool (per-1k-views pricing) |
deliverables | array | yes | At least 1 for fixed (or use quantity) |
deliverables[].title | string | yes | — |
deliverables[].description | string | no | — |
deliverables[].amount | number | yes | Must be > 0 for fixed |
deliverables[].due_date | datetime | no | — |
deliverables[].scheduled_post_date | datetime | no | When the influencer posts; triggers reminders |
deliverables[].brand_media_urls | string[] | post_only | Pre-made media for this deliverable |
deliverables[].brand_caption | string | post_only | Caption/text for this deliverable |
Response 201 — the created Brief object.
Validation rules
fixed: every deliverable needsamount > 0;total_budgetis derived.cpm:total_budget > 0is required; deliverable amounts are ignored.post_only: each deliverable needsbrand_media_urlsorbrand_caption.
List your own briefs (newest first). Response is an array of Brief objects each with an added remaining_budget field.
Fetch one brief with its full deliverables array.
Fund the brief from your prepaid wallet. Debits the budget instantly and opens the brief for applications. No request body.
Response 200
{ "funded": true, "balance_after": 4900.0 }Errors
- 409 if the brief is not in
draftstatus. - 409 if the wallet balance is insufficient (message includes the shortfall).
Deliverables
Append deliverables to a draft brief and recompute its budget.
Body
{
"deliverables": [
{ "title": "Unboxing + first impressions", "amount": 100.00, "brand_media_urls": ["https://.../unbox.mp4"], "brand_caption": "Full unboxing" }
]
}Response 200 — updated Brief object.
Errors
- 409 if the brief is not in
draftstatus.
Attach (or replace) brand-supplied content on a post-only deliverable. Upload files via the platform upload endpoint first, then pass the returned URLs here.
Body
{ "media_urls": ["https://cdn.example.com/launch.mp4"], "caption": "Spring is here!" }Response 200 — the updated deliverable
{
"id": "a671c830-...",
"title": "Spring Launch",
"amount": 10,
"status": "unassigned",
"brand_media_urls": ["https://cdn.example.com/launch.mp4"],
"brand_caption": "Spring is here!"
}Applicants
Pending creator applications for a brief.
Response 200
[
{
"id": "8f9c...",
"brief_id": "e1b2...",
"creator_id": "3f0f...",
"pitch": "Love this brand — perfect niche fit!",
"status": "pending",
"creator": { "id": "3f0f...", "display_name": "Maya Wellness", "rate": 1500.0, "niches": ["health"], "types": ["creator"] }
}
]Invites
Privately invite a creator to specific deliverables.
Body
{
"creator_id": "3f0f...",
"deliverable_ids": ["d1a2...", "d2b3..."],
"proposed_amount": 100.00,
"message": "Hi Maya — we'd love you on this!"
}Fields
| Field | Type | Required | Notes |
|---|---|---|---|
creator_id | uuid | yes | From GET /creators or applicants |
deliverable_ids | array | yes | ≥ 1 unassigned deliverable of this brief |
proposed_amount | number | no | Defaults to the deliverable amount |
message | string | no | — |
Response 201 — the invite object.
Errors
- 409 if a deliverable is already assigned, or the brief is not open.
Select (propose terms)
Propose terms to a pending applicant for one deliverable, opening a negotiation.
Body
{
"creator_id": "3f0f...",
"deliverable_id": "d1a2...",
"agreed_amount": 100.00,
"notes": "Any additional guidance"
}Response 201 — an Agreement object in negotiating status. The creator must accept (in the app) before work starts and before budget is committed.
Errors
- 404 if there is no pending application from that creator.
Submissions
All submissions for a brief (drafts and proofs), newest first. Each entry preloads its assignment, assignment.creator, and assignment.deliverable.
Response 200
[
{
"id": "c4d5...",
"assignment_id": "a1b2...",
"kind": "draft",
"content_links": ["https://..."],
"screenshot_urls": [],
"notes": "Ready for review",
"reported_views": 0,
"verified_views": null,
"status": "pending_review",
"assignment": {
"id": "a1b2...",
"agreed_amount": 100.0,
"status": "awaiting_brand_review",
"creator": { "id": "3f0f...", "display_name": "Maya Wellness" },
"deliverable": { "id": "d1a2...", "title": "Gym lookbook video" }
}
}
]Submission kinds & statuses
| Kind | Meaning |
|---|---|
draft | Pre-posting draft, awaiting your review (only when requires_brand_approval) |
proof | Posted content, verified by our team before payment |
Submission statuses: pending_review, approved, rejected, changes_requested, revision_requested.
Brand review (drafts)
Approve or request changes on a draft submission.
Body
{ "status": "approved", "notes": "Looks great, post it!" }status is one of: approved | changes_requested | rejected.
Response 200 — the BrandReview record.
Errors
- 409 if the submission is not a draft or was already reviewed.
- 403 if the submission belongs to another account.
Confirm & pay
Final sign-off after our team approves the proof. Releases the held escrow payment to the creator.
No request body.
Response 200 — the released Payment object.
Errors
- 409 if the submission is not
approvedor the assignment is already paid. - 403 if the submission belongs to another account.
Object reference
Brief object
{
"id": "e1b2...",
"title": "Summer Fitness Campaign",
"description": "UGC videos for our activewear line",
"total_budget": 200.0,
"allocated_budget": 0.0,
"remaining_budget": 200.0,
"currency": "USD",
"deadline": "2026-12-31T23:59:59Z",
"required_niche": "health",
"status": "draft",
"selection_mode": "brand_selects",
"pricing_model": "fixed",
"requires_brand_approval": true,
"requires_shipping": false,
"deliverables": [
{
"id": "d1a2...",
"title": "Gym lookbook video",
"description": "60s vertical video",
"amount": 100.0,
"currency": "USD",
"due_date": "2026-11-30T18:00:00Z",
"scheduled_post_date": "2026-12-05T18:00:00Z",
"sequence": 0,
"status": "unassigned"
}
]
}Statuses
Brief statuses: draft → open → in_progress → completed | cancelled | expired (after deadline + grace_hours; new work blocked, in-flight finishes).
Deliverable statuses: unassigned → assigned → in_progress → completed | cancelled.
Assignment statuses: assigned, proposed, awaiting_brand_review, changes_requested, awaiting_post, posted, revision_requested, approved, paid, cancelled.
Endpoint summary
| Method | Path | Description |
|---|---|---|
| GET | /creators | Creator/clipper directory |
| POST | /briefs | Create a brief (deliverables[] or quantity; post_only support) |
| GET | /briefs | List my briefs |
| GET | /briefs/:id | Brief + deliverables |
| POST | /briefs/:id/checkout | Fund from wallet (instant debit) |
| POST | /briefs/:id/deliverables | Append deliverables (draft only) |
| POST | /briefs/:id/deliverables/:did/content | Attach brand-supplied content (post_only) |
| GET | /briefs/:id/applicants | Pending applicants |
| POST | /briefs/:id/invite | Invite a creator |
| POST | /briefs/:id/select | Propose terms to an applicant |
| GET | /briefs/:id/submissions | Drafts + proofs for a brief |
| POST | /submissions/:id/review | Review a draft |
| POST | /submissions/:id/confirm | Confirm & release payment |
| GET | /wallet | Prepaid balances (all currencies) |
| POST | /wallet/top-up | Top up wallet (hosted checkout) |
| GET | /wallet/top-up/:id/status | Poll/verify a top-up |
| GET | /wallet/transactions | Wallet ledger |
Usage & limits
- Every API call is logged (license, endpoint, status) for auditing.
- Keys are enforced for expiry on every request — an expired license immediately returns 401.
- Rate limits may be applied per license. Plan for idempotent retries with backoff on 429 (when enabled) and 5xx.
Support
- Contact support at onboarding with your license ID (visible in your dashboard).
- Report bugs with the request/response payloads and timestamps; request logs help us investigate.