Async Jobs
Some operations take time: publishing a listing requires translating the description, processing geolocation, and optimizing photos. Instead of making you wait, the API runs these tasks in the background and returns a jobId for tracking.
When jobId Is Returned
| Operation | What Happens in Background |
|---|---|
| Publishing a listing | Description translation, location processing, photo analysis |
| Uploading media | Optimization, thumbnail generation |
How It Works
1. Request Returns jobId
When publishing a listing, the status changes to pending_active, and a jobId is returned:
{
"id": 42,
"externalId": "APT-001",
"status": "pending_active",
"publicUrl": "https://rentix.md/announcement/42",
"updated": true,
"jobId": 789
}
2. Check Job Status
Use GET /job/status to track progress:
async function waitForJob(jobId) {
while (true) {
const response = await fetch(
`https://crm.rentix.md/api/v1/job/status?id=${jobId}`,
{ headers: { 'Authorization': 'ApiKey YOUR_API_KEY' } }
);
const job = await response.json();
if (job.status === 'completed') {
console.log('Job completed:', job.resultData);
return job;
}
if (job.status === 'failed') {
throw new Error(job.publicErrorMessage);
}
// Wait one second before the next check
await new Promise(r => setTimeout(r, 1000));
}
}
const job = await waitForJob(789);
curl "https://crm.rentix.md/api/v1/job/status?id=789" \
-H "Authorization: ApiKey YOUR_API_KEY"
function waitForJob($jobId) {
while (true) {
$ch = curl_init("https://crm.rentix.md/api/v1/job/status?id=$jobId");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: ApiKey YOUR_API_KEY']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$job = json_decode($response, true);
if ($job['status'] === 'completed') {
return $job;
}
if ($job['status'] === 'failed') {
throw new Exception($job['publicErrorMessage']);
}
sleep(1);
}
}
3. Job Statuses
| Status | Description | Action |
|---|---|---|
pending | Queued | Wait |
processing | Running | Wait |
completed | Success | Check resultData |
failed | Error | Check publicErrorMessage |
failed_retryable | Temporary error | Can retry the operation |
Response Examples
Job Queued
{
"id": 789,
"type": "announcement_finalize",
"status": "pending",
"queuePosition": 3,
"createdAt": "2026-02-08T12:00:00.000Z"
}
queuePosition: 3 — there are 3 jobs ahead of yours in the queue.
Job Running
{
"id": 789,
"type": "announcement_finalize",
"status": "processing",
"queuePosition": null,
"createdAt": "2026-02-08T12:00:00.000Z"
}
Job Completed
{
"id": 789,
"type": "announcement_finalize",
"status": "completed",
"resultData": {
"announcementId": 42,
"completedOperations": ["translate_description", "process_location", "analyze_images"],
"finalStatus": "active"
},
"createdAt": "2026-02-08T12:00:00.000Z",
"completedAt": "2026-02-08T12:00:15.000Z"
}
After successful finalization, the listing status becomes active.
Job Failed
{
"id": 789,
"type": "announcement_finalize",
"status": "failed",
"publicErrorMessage": "Failed to process images",
"isRetryable": true,
"createdAt": "2026-02-08T12:00:00.000Z",
"completedAt": "2026-02-08T12:00:10.000Z"
}
When isRetryable: true, you can retry the operation — send the same publish request.
Response Fields
| Field | Type | Description |
|---|---|---|
id | number | Job ID |
type | string | Operation type |
status | string | Current status |
resultData | object | Result (when completed) |
publicErrorMessage | string | Error message |
isRetryable | boolean | Whether retry is possible |
queuePosition | number | Position in queue |
createdAt | string | Creation time |
completedAt | string | Completion time |
Recommendations
Polling Frequency
Don't poll status more than once per second — this creates unnecessary load and doesn't speed up execution.
Logging
Save jobId in your logs. This helps with diagnostics — support can find the job by ID.
Error Handling
Check isRetryable. If true — it's a temporary issue, you can retry. If false — you need to fix the data.
Typical Publishing Scenario
1. PUT /listings { externalId: "APT-001", announcementStatus: "active" }
→ status: "pending_active", jobId: 789
2. GET /job/status?id=789
→ status: "pending", queuePosition: 3
3. GET /job/status?id=789 (after 5 sec)
→ status: "processing"
4. GET /job/status?id=789 (after 10 sec)
→ status: "completed", resultData.finalStatus: "active"
5. Listing is published on rentix.md