API v1Back to platform
CAREERX PARTNER API

Build on good work.

Connect portfolios, services and hiring to your product. A straightforward REST API with scoped access and reliable event delivery.

REST + JSONScoped API keysSigned webhooks
01 / GET CONNECTED

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.

Your first request
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.

Store keys in server environment variables. Keys and webhook signing secrets are shown once when issued. Revoking a key also stops future webhook delivery.
02 / THE BASICS

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.

Paginated response
{
  "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.

StatusMeaningNext step
400VALIDATION_ERRORCorrect the request fields.
401UNAUTHORIZEDSupply a valid key.
403FORBIDDENCheck scopes, expiry and key ownership.
404NOT_FOUNDCheck the identifier and integration owner.
409CONFLICTCheck current state; keep a submitted payment hash.
413PAYLOAD_TOO_LARGEReduce the request body.
429RATE_LIMITEDWait for Retry-After.
503UNAVAILABLERetry later with the same idempotency key.
Error response
{
  "success": false,
  "error": {
    "code": "FORBIDDEN",
    "message": "Required scope: jobs:write."
  }
}
03 / API REFERENCE

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

JSON request body
{
  "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

JSON request body
{
  "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

JSON request body
{
  "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

JSON request body
{
  "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

JSON request body
{
  "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

JSON request body
{
  "reason": "The agreed deliverables are incomplete."
}
04 / PAYMENTS

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.

  1. 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.
  2. 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.
  3. Confirm the deposit. Save the transaction hash immediately. Send it with the quote ID to /escrow/deposit. A 409 may mean it needs more confirmations. Retry the same hash.
  4. Release and confirm. The client signs confirmAndRelease(escrow_id_onchain). Submit the release hash to the release endpoint to update the record after verification.
A dispute is a report for review. It does not freeze the contract or reverse a completed transfer. Older escrow records without verified chain details require reconciliation.
05 / STAY IN SYNC

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.completed

Events 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.

Node.js / Express
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);
06 / YOUR NEXT BUILD

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.

Requests are linked to your CareerX account and reviewed by the team.