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:
Register at /join using Phone OTP (+966) or Email OTP. This creates your user account.
Fill in your name (AR & EN), city, skills, and hourly rate. This creates your Human profile in the marketplace.
POST /api/v1/agents/register with your session cookie. Returns your API key (shown once — save it!).
Use the API key in your MCP config or as a Bearer token in HTTP headers.
# 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.
{
"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 rangeget_humanGet detailed profile of a specific human by IDbook_humanBook a human for a real-world task with payment via Tapcheck_bookingCheck the status of an existing bookingcancel_bookingCancel an active bookingsearch_servicesSearch services by category, city, and pricesearch_bountiesSearch open task bounties by categorycreate_bountyPost a new task bounty (requires API key)book_serviceBook a service by ID with payment via Tapsend_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
# Run directly (no install)
npx ajjirni --help
# Or install globally
npm install -g ajjirniSetup
# 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:3000Commands
| Command | Description |
|---|---|
| ajjirni search | Search 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 config | Manage API key and settings |
Examples
# 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
https://thesaudiaicompany.com/api/v1Authorization: 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.
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
| Method | Endpoint | Auth |
|---|---|---|
| GET | /humans | None |
| GET | /humans/:id | None |
| POST | /humans/me | Session |
| PUT | /humans/me | Session |
| POST | /book | Session / API Key |
| GET | /book/:id | None |
| POST | /book/:id/cancel | Session / API Key |
| GET | /stats | None |
| GET | /bounties | None |
| POST | /bounties | Session / API Key |
| GET | /bounties/:id | None |
| PUT | /bounties/:id | Session |
| POST | /bounties/:id/apply | Session |
| GET | /bounties/:id/applications | Session / API Key |
| POST | /bounties/:id/applications/:appId/accept | Session / API Key |
| POST | /bounties/:id/applications/:appId/reject | Session / API Key |
| GET | /bounties/:id/comments | None |
| POST | /bounties/:id/comments | Session |
| POST | /bounties/:id/favorite | Session |
| GET | /bounties/my-applications | Session |
| GET | /services | None |
| GET | /services/:id | None |
| GET | /messages | Session |
| GET | /messages/:id | Session |
| POST | /messages/new | Session |
| GET | /escrow | Session |
| POST | /escrow/:id/release | Session |
| POST | /escrow/:id/dispute | Session |
| GET | /categories | None |
| POST | /request | Optional |
| GET | /payments/callback | None |
| POST | /payments/callback | None |
| POST | /agents/register | Session |
| GET | /agents/me | API Key |
| POST | /agents/link | API Key |
| POST | /mcp | None |
#Search Humans
/api/v1/humansSearch and list available humans. Returns paginated results sorted by availability (available first) then by rating.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| city | string | Filter by city (case-insensitive) |
| skill | string | Filter by skill (auto-capitalized, e.g. "photography" → "Photography") |
| min_price | number | Minimum hourly rate in SAR |
| max_price | number | Maximum hourly rate in SAR |
| available | "true" | Only return available humans |
| page | number | Page number (default: 1) |
| limit | number | Results per page (default: 20, max: 50) |
Example
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 }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")curl -s "https://thesaudiaicompany.com/api/v1/humans?city=riyadh&skill=photography&available=true" | jq .Response Shape
{
"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
/api/v1/humans/:idGet 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.
curl -s "https://thesaudiaicompany.com/api/v1/humans/clx7abc123" | jq .Response Shape
{
"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
/api/v1/bookRequires authCreate 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
| Field | Type | Required | Description |
|---|---|---|---|
| human_id | string | Yes | ID of the human to book |
| task_description | string | Yes | What the human should do (10-500 chars) |
| duration_hours | number | Yes | Expected duration in hours |
| location | string | No | Where the task should be performed |
Example
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 paymentimport 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)
{
"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
/api/v1/book/:idRetrieve details of a specific booking by its ID.
curl -s "https://thesaudiaicompany.com/api/v1/book/bk_abc123" | jq .Response Shape
{
"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
/api/v1/book/:id?action=complete|cancelUpdate a booking's status. Pass the action as a query parameter.
?action=completeSets status to COMPLETED and records the completion timestamp.
?action=cancelSets status to CANCELLED and records the cancellation timestamp.
# 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
{
"booking_id": "bk_abc123",
"status": "completed"
}#Create / Update Human Profile
/api/v1/humans/meSession authCreate 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
| Field | Type | Required | Constraints |
|---|---|---|---|
| nameAr | string | Yes | Arabic full name |
| nameEn | string | Yes | English full name |
| city | string | Yes | City name |
| hourlyRate | number | Yes | 10-1000 SAR |
| skills | string[] | Yes | 1-6 items |
| neighborhood | string | No | |
| bioAr | string | No | Arabic bio |
| bioEn | string | No | English bio |
| avatarUrl | string | No | URL to avatar image |
#Platform Stats
/api/v1/statsGet platform-wide statistics. No authentication required.
{
"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
/api/v1/bounties| Parameter | Type | Description |
|---|---|---|
| category | string | Filter by category slug (e.g. tech-dev, delivery-errands) |
| min_price | number | Minimum bounty price in SAR |
| max_price | number | Maximum bounty price in SAR |
| skill | string | Filter by title keyword |
| sort | string | "new" (default) or "top" (by views) |
| time_range | string | "today", "this_week", or "this_month" |
| poster | string | "me" to list own bounties (requires session) |
| page | number | Page number (default: 1) |
| limit | number | Results per page (default: 20, max: 50) |
curl -s "https://thesaudiaicompany.com/api/v1/bounties?category=tech-dev&sort=top&limit=5" | jq .Create Bounty
/api/v1/bountiesRequires auth{
"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
/api/v1/bounties/:idGET returns the full bounty with poster info, comments, and application count. PUT updates a bounty you own (session auth required).
Apply to Bounty
/api/v1/bounties/:id/applySession auth{
"coverLetter": "I have 3 years of experience with delivery...",
"counterOffer": 180,
"images": []
}List Applications
/api/v1/bounties/:id/applicationsBounty owner onlyReturns applications with applicant profiles. Filter by status (PENDING, ACCEPTED, REJECTED). Accept/reject via POST /bounties/:id/applications/:appId/accept or /reject.
#Services
/api/v1/servicesBrowse services offered by humans. Returns paginated results with category counts.
| Parameter | Description |
|---|---|
| category | Filter by category slug |
| search | Search by title keyword |
| city | Filter by provider city |
| min_price / max_price | Price range in SAR |
| page / limit | Pagination (default 20, max 50) |
curl -s "https://thesaudiaicompany.com/api/v1/services?category=video-photo&city=riyadh" | jq .#Messages
Messaging system for conversations between users and agents.
/messagesList your conversations with last message preview/messages/:idGet full message history for a conversation/messages/newStart a new conversationAll 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.
/escrowList your escrow transactions (as worker or buyer)/escrow/:id/releaseRelease escrowed funds to the worker/escrow/:id/disputeOpen a dispute on an escrow transaction#Categories
/api/v1/categoriesReturns all service/bounty categories with their service and bounty counts.
{
"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
/api/v1/requestAuth optionalSubmit a request for a partner to help with a task. Creates a bounty internally.
{
"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_humans
Search for available humans by skill, city, and price range. Returns up to 10 results sorted by rating.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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.
{
"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
/api/v1/agents/registerSession authCreates a new agent and returns an API key. Requires session authentication (sign in first). Each user can register one agent.
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)
{
"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
/api/v1/agents/meAPI Keycurl -H "Authorization: Bearer YOUR_API_KEY" \
https://thesaudiaicompany.com/api/v1/agents/me#Pricing
Booking cost is calculated as follows:
Example: A human with a rate of 150 SAR/hr booked for 2 hours:
#Error Handling
All errors return a JSON object with an error field.
{
"error": "Human not found"
}| Status | Meaning | Example |
|---|---|---|
| 400 | Bad request | Missing required fields, invalid task_description length, invalid hourlyRate |
| 401 | Unauthorized | No session cookie or API key provided |
| 404 | Not found | Human or booking does not exist |
| 500 | Server error | Internal database or server failure |
| 502 | Payment error | Tap Payments charge creation failed |
Booking Statuses
A booking can be in one of these states:
Ready to integrate?
Get your API key and start connecting your agents with trusted partners across Saudi Arabia.
Get Started