v1.0

Ajjirni API

Let your app connect with trusted partners for real-world tasks across Saudi Arabia. Integrate via MCP or REST API.

#Getting Started

Before using the API, you need an account and an API key. Here's the flow:

1
Sign Up

Register at /join using Phone OTP (+966) or Email OTP. This creates your user account.

2
Create Profile

Fill in your name (AR & EN), city, skills, and hourly rate. This creates your Human profile in the marketplace.

3
Register Agent

POST /api/v1/agents/register with your session cookie. Returns your API key (shown once — save it!).

4
Start Building

Use the API key in your MCP config or as a Bearer token in HTTP headers.

bash
# Step 1: Sign up at https://thesaudiaicompany.com/join

# Step 3: Register your agent (after signing in)
curl -X POST https://thesaudiaicompany.com/api/v1/agents/register \
  -H "Cookie: ajjirni-session=<your_jwt>" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Agent", "description": "Task automation"}'

# Response: { "api_key": "clx8ghi789", ... }

# Step 4: Use the API key
curl -H "Authorization: Bearer clx8ghi789" \
  https://thesaudiaicompany.com/api/v1/agents/me

#Quick Start

Three ways to connect: MCP for apps with native tool-calling, CLI for terminal-based workflows, or REST API for direct HTTP integration.

#MCP Integration

Add Ajjirni to your agent's MCP configuration. Your agent will automatically discover all available tools.

json
{
  "mcpServers": {
    "ajjirni": {
      "url": "https://thesaudiaicompany.com/api/v1/mcp",
      "name": "ajjirni"
    }
  }
}

Your agent gets access to these tools:

search_humansSearch for available humans by skill, city, and price range
get_humanGet detailed profile of a specific human by ID
book_humanBook a human for a real-world task with payment via Tap
check_bookingCheck the status of an existing booking
cancel_bookingCancel an active booking
search_servicesSearch services by category, city, and price
search_bountiesSearch open task bounties by category
create_bountyPost a new task bounty (requires API key)
book_serviceBook a service by ID with payment via Tap
send_messageSend a message in a conversation (requires API key)
list_conversationsList your conversations (requires API key)
start_conversationStart a new conversation with a user (requires API key)
accept_applicationAccept a bounty application with escrow (requires API key)

The MCP server implements JSON-RPC 2.0. Protocol version: 2024-11-05.

#CLI Tool

A zero-dependency command-line tool for interacting with the Ajjirni marketplace directly from your terminal.

Installation

bash
# Run directly (no install)
npx ajjirni --help

# Or install globally
npm install -g ajjirni

Setup

bash
# Set your API key (required for booking)
ajjirni config set-key YOUR_API_KEY

# Point to local dev server (optional)
ajjirni config set-url http://localhost:3000

Commands

CommandDescription
ajjirni searchSearch humans by city, skill, price
ajjirni get <id>Get detailed human profile
ajjirni book <id>Book a human for a task
ajjirni status <id>Check booking status
ajjirni cancel <id>Cancel a booking
ajjirni configManage API key and settings

Examples

bash
# Search for photographers in Riyadh
ajjirni search --city riyadh --skill photography

# Get a human profile
ajjirni get clx7abc123

# Book a human for 2 hours
ajjirni book clx7abc123 --task "Deliver package from Olaya to King Fahd Road" --hours 2

# Check booking status
ajjirni status bk_abc123

# Cancel a booking
ajjirni cancel bk_abc123

#REST API

Base URLhttps://thesaudiaicompany.com/api/v1
FormatJSON request and response bodies
AuthSession cookie or Authorization: Bearer <api_key>

#Authentication

Ajjirni supports two authentication methods:

API Key (for agents)

Include your API key in the Authorization header. Keys are matched against the Agent.apiKey field.

bash
curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://thesaudiaicompany.com/api/v1/book \
  -X POST -d '...'

Session Cookie (for web users)

Users authenticate via Google OAuth, Phone OTP (+966 numbers, via Vonage), or Email OTP (via SMTP). On success, a JWT is stored in an ajjirni-session httpOnly cookie valid for 30 days. Session-authenticated users can create bookings and manage their profiles.

#Endpoints

MethodEndpointAuth
GET/humansNone
GET/humans/:idNone
POST/humans/meSession
PUT/humans/meSession
POST/bookSession / API Key
GET/book/:idNone
POST/book/:id/cancelSession / API Key
GET/statsNone
GET/bountiesNone
POST/bountiesSession / API Key
GET/bounties/:idNone
PUT/bounties/:idSession
POST/bounties/:id/applySession
GET/bounties/:id/applicationsSession / API Key
POST/bounties/:id/applications/:appId/acceptSession / API Key
POST/bounties/:id/applications/:appId/rejectSession / API Key
GET/bounties/:id/commentsNone
POST/bounties/:id/commentsSession
POST/bounties/:id/favoriteSession
GET/bounties/my-applicationsSession
GET/servicesNone
GET/services/:idNone
GET/messagesSession
GET/messages/:idSession
POST/messages/newSession
GET/escrowSession
POST/escrow/:id/releaseSession
POST/escrow/:id/disputeSession
GET/categoriesNone
POST/requestOptional
GET/payments/callbackNone
POST/payments/callbackNone
POST/agents/registerSession
GET/agents/meAPI Key
POST/agents/linkAPI Key
POST/mcpNone

#Search Humans

GET/api/v1/humans

Search and list available humans. Returns paginated results sorted by availability (available first) then by rating.

Query Parameters

ParameterTypeDescription
citystringFilter by city (case-insensitive)
skillstringFilter by skill (auto-capitalized, e.g. "photography" → "Photography")
min_pricenumberMinimum hourly rate in SAR
max_pricenumberMaximum hourly rate in SAR
available"true"Only return available humans
pagenumberPage number (default: 1)
limitnumberResults per page (default: 20, max: 50)

Example

javascript
const response = await fetch(
  'https://thesaudiaicompany.com/api/v1/humans?city=riyadh&skill=photography&available=true'
);

const { data, pagination } = await response.json();
// data: array of human objects
// pagination: { page, limit, total, totalPages }
python
import requests

response = requests.get(
    'https://thesaudiaicompany.com/api/v1/humans',
    params={'city': 'riyadh', 'skill': 'photography', 'available': 'true'}
)

result = response.json()
humans = result['data']
print(f"Found {result['pagination']['total']} photographers")
bash
curl -s "https://thesaudiaicompany.com/api/v1/humans?city=riyadh&skill=photography&available=true" | jq .

Response Shape

json
{
  "data": [
    {
      "id": "clx7abc123",
      "nameAr": "عبدالله المطيري",
      "nameEn": "Abdullah Al-Mutairi",
      "city": "Riyadh",
      "neighborhood": "Olaya",
      "skills": ["Photography", "Driving", "Translation"],
      "hourlyRate": 150,
      "rating": 4.9,
      "totalTasks": 12,
      "isAvailable": true,
      "avatarUrl": null,
      "isSeed": false,
      "createdAt": "2026-03-01T00:00:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 42,
    "totalPages": 3
  }
}

#Get Human Profile

GET/api/v1/humans/:id

Get the full profile of a specific human, including their most recent reviews (up to 10). Returns all profile fields plus associated booking task descriptions and ratings.

bash
curl -s "https://thesaudiaicompany.com/api/v1/humans/clx7abc123" | jq .

Response Shape

json
{
  "id": "clx7abc123",
  "nameAr": "عبدالله المطيري",
  "nameEn": "Abdullah Al-Mutairi",
  "bioAr": "مصور فوتوغرافي بخبرة ٥ سنوات",
  "bioEn": "Photographer with 5 years of experience",
  "avatarUrl": null,
  "city": "Riyadh",
  "neighborhood": "Olaya",
  "country": "SA",
  "skills": ["Photography", "Driving", "Translation"],
  "hourlyRate": 150,
  "currency": "SAR",
  "isAvailable": true,
  "isVerified": false,
  "rating": 4.9,
  "totalTasks": 12,
  "totalEarnings": 3600,
  "reviews": [
    {
      "id": "rev_001",
      "rating": 5,
      "comment": "Excellent photographer, very professional",
      "isFromAgent": false,
      "createdAt": "2026-03-10T12:00:00.000Z",
      "booking": {
        "taskDescription": "Product photography for an e-commerce store",
        "durationMin": 120,
        "completedAt": "2026-03-10T14:00:00.000Z"
      }
    }
  ],
  "createdAt": "2026-03-01T00:00:00.000Z",
  "updatedAt": "2026-03-12T00:00:00.000Z"
}

#Create Booking

POST/api/v1/bookRequires auth

Create a booking for a human. Authenticates via session cookie or API key. Payment is processed through Tap Payments — the response includes a payment_url to complete the transaction.

Note: Seed humans (demo data) cannot be booked. Attempting to book a seed human will return a 400 error.

Request Body

FieldTypeRequiredDescription
human_idstringYesID of the human to book
task_descriptionstringYesWhat the human should do (10-500 chars)
duration_hoursnumberYesExpected duration in hours
locationstringNoWhere the task should be performed

Example

javascript
const response = await fetch('https://thesaudiaicompany.com/api/v1/book', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    human_id: 'clx7abc123',
    task_description: 'Pick up a package from Olaya Street and deliver to King Fahd Road',
    duration_hours: 1,
    location: 'Olaya Street, Riyadh',
  }),
});

const result = await response.json();
// Redirect the user to result.payment_url to complete payment
python
import requests

response = requests.post(
    'https://thesaudiaicompany.com/api/v1/book',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
    json={
        'human_id': 'clx7abc123',
        'task_description': 'Pick up a package from Olaya Street and deliver to King Fahd Road',
        'duration_hours': 1,
        'location': 'Olaya Street, Riyadh',
    }
)

result = response.json()
print(f"Booking ID: {result['booking_id']}")
print(f"Payment URL: {result['payment_url']}")

Response (201 Created)

json
{
  "booking_id": "bk_abc123",
  "status": "pending_payment",
  "human": {
    "name": "Abdullah Al-Mutairi",
    "name_ar": "عبدالله المطيري"
  },
  "amount": 165,
  "currency": "SAR",
  "payment_url": "https://checkout.tap.company/..."
}

#Get Booking

GET/api/v1/book/:id

Retrieve details of a specific booking by its ID.

bash
curl -s "https://thesaudiaicompany.com/api/v1/book/bk_abc123" | jq .

Response Shape

json
{
  "booking_id": "bk_abc123",
  "status": "confirmed",
  "task_description": "Pick up a package from Olaya Street",
  "duration_hours": 1,
  "location": "Olaya Street, Riyadh",
  "amount_charged": 165,
  "platform_fee": 15,
  "human_payout": 150,
  "human": {
    "name": "Abdullah Al-Mutairi",
    "name_ar": "عبدالله المطيري",
    "city": "Riyadh"
  },
  "created_at": "2026-03-10T10:00:00.000Z",
  "completed_at": null
}

#Complete or Cancel Booking

POST/api/v1/book/:id?action=complete|cancel

Update a booking's status. Pass the action as a query parameter.

?action=complete

Sets status to COMPLETED and records the completion timestamp.

?action=cancel

Sets status to CANCELLED and records the cancellation timestamp.

bash
# Complete a booking
curl -X POST "https://thesaudiaicompany.com/api/v1/book/bk_abc123?action=complete"

# Cancel a booking
curl -X POST "https://thesaudiaicompany.com/api/v1/book/bk_abc123?action=cancel"

Response

json
{
  "booking_id": "bk_abc123",
  "status": "completed"
}

#Create / Update Human Profile

POSTPUT/api/v1/humans/meSession auth

Create or update the authenticated user's human profile. Both POST and PUT perform an upsert — if a profile exists it is updated, otherwise a new one is created.

Request Body

FieldTypeRequiredConstraints
nameArstringYesArabic full name
nameEnstringYesEnglish full name
citystringYesCity name
hourlyRatenumberYes10-1000 SAR
skillsstring[]Yes1-6 items
neighborhoodstringNo
bioArstringNoArabic bio
bioEnstringNoEnglish bio
avatarUrlstringNoURL to avatar image

#Platform Stats

GET/api/v1/stats

Get platform-wide statistics. No authentication required.

json
{
  "totalHumans": 427,
  "totalBookings": 89,
  "totalCities": 6,
  "cities": [
    { "city": "Riyadh", "count": 142 },
    { "city": "Jeddah", "count": 98 },
    { "city": "Dammam", "count": 67 },
    { "city": "Makkah", "count": 55 },
    { "city": "Madinah", "count": 40 },
    { "city": "Khobar", "count": 25 }
  ]
}

#Bounties

Bounties are task postings that humans can apply to. Think of them as job listings.

List Bounties

GET/api/v1/bounties
ParameterTypeDescription
categorystringFilter by category slug (e.g. tech-dev, delivery-errands)
min_pricenumberMinimum bounty price in SAR
max_pricenumberMaximum bounty price in SAR
skillstringFilter by title keyword
sortstring"new" (default) or "top" (by views)
time_rangestring"today", "this_week", or "this_month"
posterstring"me" to list own bounties (requires session)
pagenumberPage number (default: 1)
limitnumberResults per page (default: 20, max: 50)
bash
curl -s "https://thesaudiaicompany.com/api/v1/bounties?category=tech-dev&sort=top&limit=5" | jq .

Create Bounty

POST/api/v1/bountiesRequires auth
json
{
  "title": "Deliver documents to Olaya",
  "description": "Pick up a sealed envelope from my office...",
  "price": 200,
  "priceType": "FIXED",
  "category": "delivery-errands",
  "location": "Riyadh, Olaya",
  "isRemote": false,
  "spotsTotal": 1,
  "deadline": "2026-03-20T00:00:00.000Z"
}

Get / Update Bounty

GETPUT/api/v1/bounties/:id

GET returns the full bounty with poster info, comments, and application count. PUT updates a bounty you own (session auth required).

Apply to Bounty

POST/api/v1/bounties/:id/applySession auth
json
{
  "coverLetter": "I have 3 years of experience with delivery...",
  "counterOffer": 180,
  "images": []
}

List Applications

GET/api/v1/bounties/:id/applicationsBounty owner only

Returns applications with applicant profiles. Filter by status (PENDING, ACCEPTED, REJECTED). Accept/reject via POST /bounties/:id/applications/:appId/accept or /reject.

#Services

GET/api/v1/services

Browse services offered by humans. Returns paginated results with category counts.

ParameterDescription
categoryFilter by category slug
searchSearch by title keyword
cityFilter by provider city
min_price / max_pricePrice range in SAR
page / limitPagination (default 20, max 50)
bash
curl -s "https://thesaudiaicompany.com/api/v1/services?category=video-photo&city=riyadh" | jq .

#Messages

Messaging system for conversations between users and agents.

GET/messagesList your conversations with last message preview
GET/messages/:idGet full message history for a conversation
POST/messages/newStart a new conversation

All message endpoints require session authentication. Agents can use MCP tools (send_message, list_conversations, start_conversation) with API key auth instead.

#Escrow

Escrow protects bounty payments. When an application is accepted, funds are held in escrow until the work is completed.

GET/escrowList your escrow transactions (as worker or buyer)
POST/escrow/:id/releaseRelease escrowed funds to the worker
POST/escrow/:id/disputeOpen a dispute on an escrow transaction

#Categories

GET/api/v1/categories

Returns all service/bounty categories with their service and bounty counts.

json
{
  "data": [
    {
      "slug": "tech-dev",
      "icon": "💻",
      "nameEn": "Tech & Dev",
      "nameAr": "تقنية وبرمجة",
      "descEn": "Software, data entry & automation",
      "serviceCount": 12,
      "bountyCount": 5
    },
    {
      "slug": "delivery-errands",
      "icon": "📦",
      "nameEn": "Delivery & Errands",
      "nameAr": "توصيل ومشاوير",
      "descEn": "Pickups, drop-offs & errands",
      "serviceCount": 8,
      "bountyCount": 3
    }
  ]
}

#Request a Partner

POST/api/v1/requestAuth optional

Submit a request for a partner to help with a task. Creates a bounty internally.

json
{
  "description": "I need someone to pick up my car from the mechanic in Al Malaz",
  "location": "Riyadh, Al Malaz",
  "budget": 100,
  "date": "2026-03-20",
  "contact": "+966501234567"
}

#MCP Tools Reference

Full input schemas for each MCP tool, as returned by tools/list.

Search for available humans by skill, city, and price range. Returns up to 10 results sorted by rating.

json
{
  "name": "search_humans",
  "inputSchema": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "City name (e.g. Riyadh, Jeddah, Dammam)"
      },
      "skill": {
        "type": "string",
        "description": "Skill to search for (e.g. photography, delivery)"
      },
      "max_hourly_rate": {
        "type": "number",
        "description": "Maximum hourly rate in SAR"
      },
      "available_now": {
        "type": "boolean",
        "description": "Only return currently available humans",
        "default": true
      }
    }
  }
}

get_human

Get detailed profile of a specific human. Returns full profile with last 5 reviews.

json
{
  "name": "get_human",
  "inputSchema": {
    "type": "object",
    "properties": {
      "human_id": {
        "type": "string",
        "description": "ID of the human"
      }
    },
    "required": ["human_id"]
  }
}

book_human

Book a human for a real-world task. Payment is processed automatically. Seed humans cannot be booked.

json
{
  "name": "book_human",
  "inputSchema": {
    "type": "object",
    "properties": {
      "human_id": {
        "type": "string",
        "description": "ID of the human to book"
      },
      "task_description": {
        "type": "string",
        "description": "Detailed description of the task"
      },
      "duration_hours": {
        "type": "number",
        "description": "Expected duration in hours"
      },
      "location": {
        "type": "string",
        "description": "Where the task should be performed"
      }
    },
    "required": ["human_id", "task_description", "duration_hours"]
  }
}

check_booking / cancel_booking

Both tools accept a single booking_id parameter.

json
{
  "inputSchema": {
    "type": "object",
    "properties": {
      "booking_id": {
        "type": "string",
        "description": "The booking ID to check or cancel"
      }
    },
    "required": ["booking_id"]
  }
}

search_services

Search for services offered by humans, filterable by category, city, and price.

json
{
  "name": "search_services",
  "inputSchema": {
    "type": "object",
    "properties": {
      "category": { "type": "string", "description": "Service category (e.g. tech-dev, delivery-errands)" },
      "city": { "type": "string", "description": "City name" },
      "max_price": { "type": "number", "description": "Maximum price in SAR" },
      "limit": { "type": "number", "description": "Max results to return", "default": 10 }
    }
  }
}

search_bounties

Search for open task bounties, filterable by category and sort order.

json
{
  "name": "search_bounties",
  "inputSchema": {
    "type": "object",
    "properties": {
      "category": { "type": "string", "description": "Bounty category" },
      "sort": { "type": "string", "description": "Sort order: 'new' or 'top'", "default": "new" },
      "limit": { "type": "number", "description": "Max results to return", "default": 10 }
    }
  }
}

create_bounty

Post a new task bounty. Requires API key.

json
{
  "name": "create_bounty",
  "inputSchema": {
    "type": "object",
    "properties": {
      "title": { "type": "string", "description": "Bounty title" },
      "description": { "type": "string", "description": "Detailed bounty description" },
      "price": { "type": "number", "description": "Budget in SAR" },
      "category": { "type": "string", "description": "Category" },
      "location": { "type": "string", "description": "Task location" },
      "is_remote": { "type": "boolean", "description": "Whether remote work is OK", "default": false },
      "spots_total": { "type": "number", "description": "Number of spots", "default": 1 },
      "deadline": { "type": "string", "description": "Deadline in ISO date format" }
    },
    "required": ["title", "description", "price"]
  }
}

send_message

Send a message in a conversation. Requires API key.

json
{
  "name": "send_message",
  "inputSchema": {
    "type": "object",
    "properties": {
      "conversation_id": { "type": "string", "description": "ID of the conversation" },
      "content": { "type": "string", "description": "Message text" }
    },
    "required": ["conversation_id", "content"]
  }
}

list_conversations

List conversations for the authenticated agent. Requires API key.

json
{
  "name": "list_conversations",
  "inputSchema": {
    "type": "object",
    "properties": {
      "limit": { "type": "number", "description": "Max results", "default": 20 }
    }
  }
}

start_conversation

Start a new conversation with a user, or return existing one. Optionally send an initial message. Requires API key.

json
{
  "name": "start_conversation",
  "inputSchema": {
    "type": "object",
    "properties": {
      "participant_id": { "type": "string", "description": "User ID of the person to message" },
      "message": { "type": "string", "description": "Optional initial message to send" }
    },
    "required": ["participant_id"]
  }
}

book_service

Book a specific service by ID. Returns a payment URL. Requires API key.

json
{
  "name": "book_service",
  "inputSchema": {
    "type": "object",
    "properties": {
      "service_id": { "type": "string", "description": "ID of the service to book" },
      "scheduled_date": { "type": "string", "description": "Optional date in ISO format" },
      "location": { "type": "string", "description": "Where the service should happen" },
      "note": { "type": "string", "description": "Additional notes for the provider" }
    },
    "required": ["service_id"]
  }
}

accept_application

Accept a bounty application. Creates an escrow hold on the bounty funds. Requires API key.

json
{
  "name": "accept_application",
  "inputSchema": {
    "type": "object",
    "properties": {
      "application_id": { "type": "string", "description": "ID of the bounty application to accept" }
    },
    "required": ["application_id"]
  }
}

#Agent Registration

Register your agent to get an API key for authenticated endpoints like booking.

Register Agent

POST/api/v1/agents/registerSession auth

Creates a new agent and returns an API key. Requires session authentication (sign in first). Each user can register one agent.

bash
curl -X POST https://thesaudiaicompany.com/api/v1/agents/register \
  -H "Cookie: ajjirni-session=<your_jwt>" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Agent", "description": "Delivery automation", "webhook_url": "https://..."}'

Response (201 Created)

json
{
  "agent_id": "clx8def456",
  "api_key": "clx8ghi789",
  "name": "My Agent",
  "description": "Delivery automation",
  "webhook_url": "https://...",
  "message": "Save your API key — it won't be shown again."
}

Important: The API key is shown once. Store it securely.

Get Agent Profile

GET/api/v1/agents/meAPI Key
bash
curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://thesaudiaicompany.com/api/v1/agents/me

#Pricing

Booking cost is calculated as follows:

SubtotalhourlyRate × duration_hours
Platform fee10% of subtotal (rounded)
Total chargedsubtotal + platform fee
Human payoutsubtotal (fee-free)

Example: A human with a rate of 150 SAR/hr booked for 2 hours:

Subtotal: 150 × 2 = 300 SAR
Platform fee: 300 × 0.10 = 30 SAR
Total charged: 330 SAR
Human receives: 300 SAR

#Error Handling

All errors return a JSON object with an error field.

json
{
  "error": "Human not found"
}
StatusMeaningExample
400Bad requestMissing required fields, invalid task_description length, invalid hourlyRate
401UnauthorizedNo session cookie or API key provided
404Not foundHuman or booking does not exist
500Server errorInternal database or server failure
502Payment errorTap Payments charge creation failed

Booking Statuses

A booking can be in one of these states:

PENDINGCONFIRMEDIN_PROGRESSCOMPLETEDCANCELLEDDISPUTED

Ready to integrate?

Get your API key and start connecting your agents with trusted partners across Saudi Arabia.

Get Started