Build on good work.
Connect portfolios, services and hiring to your product. A straightforward REST API with scoped access and reliable event delivery.
Authentication
Send your API key as a Bearer token from your backend. Each key belongs to a partner account and has an explicit set of scopes. API keys use the cx_live_ prefix.
curl 'https://careerx.pro/api/v1/profiles?per_page=10' \
-H "Authorization: Bearer $CAREERX_API_KEY"Public discovery uses read scopes. Private orders, applications and escrow records are limited to the integration key that created them. Another key’s records return 404. Contact the team when rotating a key that owns records.
Predictable requests
The base URL is https://careerx.pro/api/v1. Send JSON for POST requests. Successful reads return 200; new jobs, orders and quotes return 201.
Pagination & filtering
List endpoints accept page (default 1) and per_page (default 20, maximum 100). Filters run before pagination; invalid pagination returns 400.
{
"success": true,
"data": [],
"meta": {
"page": 1,
"per_page": 20,
"total": 0,
"total_pages": 0
}
}Retries & limits
Include a unique Idempotency-Key header when creating a job or service order. Retry the same request with the same key to get its existing record. Reusing a key with different data returns 409. Pending service orders do not charge the client or start their delivery deadline.
Rate limits are shared across servers and reset each minute. Read X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds). A 429 includes Retry-After in seconds. Include X-Request-ID when reporting an issue.
| Status | Meaning | Next step |
|---|---|---|
| 400 | VALIDATION_ERROR | Correct the request fields. |
| 401 | UNAUTHORIZED | Supply a valid key. |
| 403 | FORBIDDEN | Check scopes, expiry and key ownership. |
| 404 | NOT_FOUND | Check the identifier and integration owner. |
| 409 | CONFLICT | Check current state; keep a submitted payment hash. |
| 413 | PAYLOAD_TOO_LARGE | Reduce the request body. |
| 429 | RATE_LIMITED | Wait for Retry-After. |
| 503 | UNAVAILABLE | Retry later with the same idempotency key. |
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "Required scope: jobs:write."
}
}The endpoints
Expand an endpoint for its behavior and request fields. The downloadable OpenAPI document lists parameters, request schemas and response envelopes.
GET/profiles
Find talent
Search public profiles. Filter by search, skill and location before pagination.
Required scope profiles:read
GET/profiles/{username}
Read a portfolio
Public profile, experience, education, projects, skills, credentials, links and active services.
Required scope profiles:read
GET/services
Browse services
Active services. Supports search, category, min_price, max_price and max_delivery_days.
Required scope services:read
GET/services/{id}
Read a service
Service details, seller profile and recent reviews.
Required scope services:read
POST/services/{id}/order
Create an order
Create a pending order. Does not charge the client. Requires Idempotency-Key.
Required scope orders:write
{
"client_name": "Alex Morgan",
"client_email": "alex@example.com",
"requirements": "A five-page portfolio website."
}GET/orders/{id}
Read your order
Private order details for orders created by this integration key.
Required scope orders:read
GET/jobs
Browse jobs
Search jobs by category, status, budget_type, employment_type or location_type.
Required scope jobs:read
POST/jobs
Post a job
Create a job owned by the partner account. Requires Idempotency-Key.
Required scope jobs:write
{
"title": "Product designer",
"description": "Design a responsive portfolio editor.",
"category": "Design",
"budget_type": "fixed",
"budget_amount": 500,
"employment_type": "contract",
"location_type": "remote",
"currency": "USD"
}GET/jobs/{id}
Read a job
Job details and application count.
Required scope jobs:read
GET/jobs/{id}/applications
Read applications
Private applications for jobs created by this integration key. Supports status filtering.
Required scope jobs:read
POST/escrow/quote
Get a funding quote
Bind the job application, token amount and both wallets before signing. Quote expires in 30 minutes.
Required scope escrow:write
{
"job_id": "YOUR_JOB_UUID",
"application_id": "APPLICATION_UUID",
"client_wallet": "0xYOUR_CLIENT_WALLET",
"token": "USDT"
}POST/escrow/deposit
Confirm a deposit
Verify a submitted on-chain transaction against its quote. Reuse the same hash when retrying.
Required scope escrow:write
{
"quote_id": "QUOTE_UUID",
"tx_hash": "0xCONFIRMED_DEPOSIT_HASH"
}GET/escrow/{id}
Read your escrow
Funding and release status for escrow created by this integration key.
Required scope escrow:read
POST/escrow/{id}/release
Confirm a release
Record a confirmed on-chain release. The client first signs confirmAndRelease in their wallet.
Required scope escrow:write
{
"release_tx_hash": "0xCONFIRMED_RELEASE_HASH"
}POST/escrow/{id}/dispute
Report a dispute
Record a dispute for review. This action does not freeze the smart contract.
Required scope escrow:write
{
"reason": "The agreed deliverables are incomplete."
}Verify funds on chain.
Escrow endpoints verify transactions on BNB Smart Chain. Your client keeps control of their wallet and signs the transaction. An API call alone does not move funds.
- Request a quote. Send your job, application, client wallet and token to
/escrow/quote. The server returns the amount, contract, token address and freelancer wallet. Quotes are valid for 30 minutes. - Approve and deposit. In the client’s wallet, approve the returned token amount, then call
createEscrow(job_id_bytes32, freelancer_wallet, amount_raw, token_address)on the quoted contract. - Confirm the deposit. Save the transaction hash immediately. Send it with the quote ID to
/escrow/deposit. A409may mean it needs more confirmations. Retry the same hash. - Release and confirm. The client signs
confirmAndRelease(escrow_id_onchain). Submit the release hash to the release endpoint to update the record after verification.
Events you can rely on.
Subscribe when your key is issued. Events are saved with the record change and delivered to a public HTTPS endpoint. Delivery is at least once: deduplicate using X-CareerX-Delivery, which stays the same across retries.
order.createdorder.paidorder.in_progressorder.deliveredorder.completedorder.cancelledescrow.depositedescrow.releasedescrow.disputedjob.createdjob.application_receivedjob.completedEvents are emitted when the corresponding record changes. Subscriptions require the resource’s read scope. Respond with a 2xx status within 10 seconds. Failed deliveries get up to eight attempts with exponential delays; contact the team to replay exhausted deliveries. The worker must be scheduled on the deployment.
Verify the raw request body
Compute HMAC-SHA256 over the exact request bytes using your webhook secret. Verify before parsing JSON. This Express example also handles malformed signatures safely.
import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
await pool.query(
'CREATE TABLE IF NOT EXISTS incoming_careerx_events (' +
'id uuid PRIMARY KEY, payload jsonb NOT NULL, received_at timestamptz DEFAULT now())'
);
const app = express();
// Register before any app.use(express.json()) middleware.
app.post('/careerx/events', express.raw({ type: 'application/json' }),
async (req, res) => {
const signature = req.get('X-CareerX-Signature') || '';
if (!/^[a-f0-9]{64}$/i.test(signature)) return res.sendStatus(401);
const expected = createHmac('sha256', process.env.CAREERX_WEBHOOK_SECRET)
.update(req.body).digest();
const received = Buffer.from(signature, 'hex');
if (received.length !== expected.length ||
!timingSafeEqual(received, expected)) return res.sendStatus(401);
const event = JSON.parse(req.body.toString('utf8'));
// Persist once using event.id as a UNIQUE key, then acknowledge.
// Process work from your durable queue, outside this request.
await pool.query(
'INSERT INTO incoming_careerx_events(id, payload) VALUES($1, $2) ON CONFLICT(id) DO NOTHING',
[event.id, event]
);
return res.sendStatus(204);
});
app.listen(3001);Get partner access.
Tell us what you’re building. We’ll review the integration, select the right scopes and issue a key for your account.