
# CLI and cURL

A page for those who work with the Vibecode API from the terminal: shell scripts, one-off checks with the `curl` utility, server-side PHP integrations without an SDK. Full operation references are on the dedicated pages: [Entity API](./entity-api.md), [Filtering](./filtering.md), [Batch API](./batch.md), [Error codes](./errors.md).

Base URL — `https://vibecode.bitrix24.com`. Authorization — via the `X-Api-Key` header. Responses — JSON in the `{ success, data, meta }` format on success and `{ success: false, error }` on error.

## On this page

- [Environment](#environment) — the `VIBE_API_KEY` / `VIBE_URL` variables, saving to `~/.zshrc` for use across sessions
- [Authorization in curl](#authorization-in-curl) — personal key and authorization key
- [Minimal verification cycle](#minimal-verification-cycle) — create → delete with an HTTP 204 check
- [Useful curl techniques](#useful-curl-techniques) — `jq`, `-s` vs `-sS`, `--data-binary @file`, `read -s`, `X-RateLimit-*`
- [OpenAPI specification](#openapi-specification) — `/v1/openapi.json`, the per-scope slice, and the third-party `openapi-to-cli` utility
- [PHP](#php) — the `vibeRequest` template with no external dependencies
- [Error handling](#error-handling) — the `{ success: false, error }` format

## Environment

Save the key and base URL into environment variables — this removes repetition in every command and keeps the key out of your shell command history.

```bash
export VIBE_API_KEY="vibe_api_xxx..."
export VIBE_URL="https://vibecode.bitrix24.com"
```

Check — JSON with the Bitrix24 account and scopes should be returned:

```bash
curl -s -H "X-Api-Key: $VIBE_API_KEY" "$VIBE_URL/v1/me" | head -c 200
```

To persist the variables across sessions, add the `export ...` lines to `~/.zshrc` (for the `zsh` shell) or `~/.bashrc` (for `bash`) and restart the terminal.

For an authorization key (`vibe_app_...`) you additionally need a session token:

```bash
export VIBE_APP_KEY="vibe_app_xxx..."
export VIBE_SESSION_TOKEN="vibe_session_xxx..."
```

## Authorization in curl

Personal key — one header:

```bash
curl -H "X-Api-Key: $VIBE_API_KEY" "$VIBE_URL/v1/deals"
```

Authorization key — two headers, `X-Api-Key` for the application and `Authorization: Bearer` for a specific user's session:

```bash
curl -H "X-Api-Key: $VIBE_APP_KEY" \
     -H "Authorization: Bearer $VIBE_SESSION_TOKEN" \
     "$VIBE_URL/v1/deals"
```

More on key types and obtaining a session token — [Keys and authorization](./keys-auth.md).

## Minimal verification cycle

A single scenario to confirm the key works and the API responds: create a deal → delete it. The same set of operations exists for all 40+ entities — only the fields differ (see [Entity API](./entity-api.md) and the [entity reference](./entities-index.md)).

```bash
# 1. Create a deal — returns an object with the assigned id
curl -X POST "$VIBE_URL/v1/deals" \
  -H "X-Api-Key: $VIBE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "title": "CLI test", "stageId": "NEW", "categoryId": 0 }'

# 2. Delete the created deal — success = HTTP 204 with an empty body
curl -i -X DELETE "$VIBE_URL/v1/deals/<id-from-first-response>" \
  -H "X-Api-Key: $VIBE_API_KEY"
```

The success indicator for `DELETE` is the response code, not the response body. To see only the code:

```bash
curl -s -o /dev/null -w "%{http_code}\n" -X DELETE \
  -H "X-Api-Key: $VIBE_API_KEY" \
  "$VIBE_URL/v1/deals/<id>"
```

Full request examples with filters, sorting, field selection, and auto-pagination are on the page for the relevant entity, for example [Deals](./entities/deals.md). Filter syntax (MongoDB-style, prefixes, operators as keys) — [Filtering and search](./filtering.md).

## Useful curl techniques

### Readable JSON in the terminal

`curl` outputs the response on a single line. To view it, pipe it through `jq` or `python3 -m json.tool`:

```bash
curl -s -H "X-Api-Key: $VIBE_API_KEY" "$VIBE_URL/v1/deals?limit=5" | jq
curl -s -H "X-Api-Key: $VIBE_API_KEY" "$VIBE_URL/v1/deals?limit=5" | python3 -m json.tool
```

### `-s` vs `-sS` in scripts

The `-s` flag (silent mode) suppresses the progress indicator along with network error messages — for example, `Could not resolve host`. In scripts this hides real failures. The `-sS` flag suppresses only the progress indicator, while the error text is written to `stderr`:

```bash
# Silent, but on a network error the script silently gets an empty response
curl -s "$VIBE_URL/v1/deals" -H "X-Api-Key: $VIBE_API_KEY"

# Silent for normal operation, network errors are visible
curl -sS "$VIBE_URL/v1/deals" -H "X-Api-Key: $VIBE_API_KEY"
```

In shell scripts use `-sS`: network errors go to `stderr` and won't be lost during debugging.

### A large request body via `--data-binary @file.json`

With a long body (a batch of 50 operations, an OpenAPI import, an expanded filter) the `-d` flag can't handle shell quote escaping and line breaks. Save the body to a file and pass it via `@`:

```bash
cat > body.json <<'EOF'
{
  "calls": [
    { "id": "newDeals", "entity": "deals", "action": "list",
      "params": { "filter": { "stageId": "NEW" }, "limit": 100 } },
    { "id": "user", "entity": "users", "action": "get", "entityId": 1 }
  ]
}
EOF

curl -X POST "$VIBE_URL/v1/batch" \
  -H "X-Api-Key: $VIBE_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @body.json
```

`--data-binary` preserves line breaks and quotes as-is, unlike `-d`, which strips out `\n`.

The response groups results by the `id` of each call — `results` holds the data, `meta` holds pagination statistics, `summary` holds the totals for the whole batch:

```json
{
  "success": true,
  "data": {
    "results": {
      "newDeals": [ { "id": 7720, "title": "Server delivery", "stageId": "NEW" } ],
      "user": { "id": 1, "name": "John Brown" }
    },
    "totals": { "newDeals": 311 },
    "meta": {
      "newDeals": { "total": 311, "returned": 100, "hasMore": true }
    },
    "errors": [],
    "summary": { "total": 2, "succeeded": 2, "failed": 0 }
  }
}
```

Full batch rules (the limit of 50 calls, the cost in rate-limit units, the behavior of `action: "list"` with `limit > 50`) — [Batch API](./batch.md).

### Exporting a key without saving it in shell history

The `export VIBE_API_KEY="vibe_api_..."` command will be saved to `~/.zsh_history` or `~/.bash_history` — from there the key can end up on a screen share or in a system backup. An alternative is `read -s`: the shell reads the key without displaying it on screen, and only the `read` command remains in history, without the value.

```bash
read -s VIBE_API_KEY && export VIBE_API_KEY
# the cursor doesn't move, paste the key, press Enter
```

The same technique works for `VIBE_APP_KEY` and `VIBE_SESSION_TOKEN`.

### Headers and response code

```bash
# -i — headers + body
curl -i -H "X-Api-Key: $VIBE_API_KEY" "$VIBE_URL/v1/me"

# -w — only selected statistics fields (code, time)
curl -s -o /dev/null -w "HTTP %{http_code}, %{time_total}s\n" \
  -H "X-Api-Key: $VIBE_API_KEY" "$VIBE_URL/v1/deals"
```

### Request limits

The response headers carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` — the current remaining request limit for the key:

```bash
curl -s -D - -o /dev/null -H "X-Api-Key: $VIBE_API_KEY" "$VIBE_URL/v1/me" | grep -i ratelimit
```

### Saving the response to a file

```bash
curl -s -H "X-Api-Key: $VIBE_API_KEY" "$VIBE_URL/v1/openapi.json" -o openapi.json
```

## OpenAPI specification

The full machine-readable catalog of endpoints — `/v1/openapi.json` in OpenAPI 3.1 format:

```bash
curl -s "$VIBE_URL/v1/openapi.json" -o openapi.json
```

The file imports into Postman, Insomnia, Swagger UI, and any tools that support OpenAPI 3.1. Based on the specification you can generate client libraries (`openapi-generator`, `oazapfts`, and similar). A tool that reads 3.0 only will not accept this specification: nullable values in it are described as a union of types (`["number","null"]`), a form introduced in 3.1.

### Per-scope slice

The `?scope=<scope>` parameter returns the specification narrowed down to the entities of a single scope — service and meta endpoints stay in every slice. The file comes out many times smaller than the full one, which helps when a tool or a model works with a single section:

```bash
curl -s "$VIBE_URL/v1/openapi.json?scope=crm" -o openapi-crm.json
```

Exactly one known scope from the [Scopes](/docs/scopes) list is accepted. An unknown value and an empty string return the full specification rather than an error. A comma-separated list does the same — unless every listed value collapses to a single scope (`task,tasks`), in which case the slice is returned.

### The `openapi-to-cli` utility

> A third-party tool, not part of the Vibecode API. The platform does not monitor its availability. For updates, bugs, and compatibility with the specification, contact the [author's repository](https://github.com/EvilFreelancer/openapi-to-cli).

[`openapi-to-cli`](https://github.com/EvilFreelancer/openapi-to-cli) is a CLI utility that builds commands from any OpenAPI specification. It connects to the Vibecode API without code generation: you provide the specification URL and the authorization header.

```bash
npm install -g openapi-to-cli

ocli profile add vibecode \
  --spec https://vibecode.bitrix24.com/v1/openapi.json \
  --base-url https://vibecode.bitrix24.com \
  --header "X-Api-Key: $VIBE_API_KEY"

# Search for an endpoint by word
ocli vibecode search "deals"

# Call
ocli vibecode exec GET /v1/deals
ocli vibecode exec POST /v1/deals \
  --body '{ "title": "New deal", "stageId": "NEW", "categoryId": 0 }'
```

The utility is third-party, maintained by its author — for updates and bugs contact its repository. After changes in the Vibecode API, the updated specification is picked up by `ocli` on the next run.

## PHP

A minimal template for scripts and server-side integrations — no external dependencies, only the built-in `curl`. The same template works for all endpoints: `vibeRequest($url, $method, $data)`.

```php
<?php

$VIBE_URL = getenv('VIBE_URL') ?: 'https://vibecode.bitrix24.com';
$VIBE_API_KEY = getenv('VIBE_API_KEY') ?: 'vibe_api_xxx...';

function vibeRequest(string $url, string $method = 'GET', ?array $data = null): array {
    global $VIBE_API_KEY;

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST  => $method,
        CURLOPT_HTTPHEADER     => [
            'X-Api-Key: ' . $VIBE_API_KEY,
            'Content-Type: application/json',
        ],
    ]);
    if ($data !== null) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
    }

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    // DELETE returns 204 with an empty body
    if ($httpCode === 204) {
        return ['success' => true];
    }

    $body = json_decode($response, true);
    if ($body === null) {
        throw new RuntimeException("HTTP $httpCode: malformed JSON in response");
    }
    if (empty($body['success'])) {
        $code = $body['error']['code'] ?? 'UNKNOWN';
        $msg  = $body['error']['message'] ?? 'Unknown error';
        throw new RuntimeException("$code: $msg (HTTP $httpCode)");
    }
    return $body;
}
```

### Usage examples

```php
// List
$result = vibeRequest("$VIBE_URL/v1/deals?limit=20");
foreach ($result['data'] as $deal) {
    echo $deal['id'] . ': ' . $deal['title'] . PHP_EOL;
}

// Create
$result = vibeRequest("$VIBE_URL/v1/deals", 'POST', [
    'title'      => 'Server delivery',
    'stageId'    => 'NEW',
    'categoryId' => 0,
    'amount'     => 1500000,
    'currency'   => 'USD',
]);
$dealId = $result['data']['id'];

// Search with a filter
$result = vibeRequest("$VIBE_URL/v1/deals/search", 'POST', [
    'filter' => [
        'stageId' => ['$ne' => 'LOST'],
        'amount'  => ['$gte' => 100000],
    ],
    'sort'  => ['createdAt' => 'desc'],
    'limit' => 50,
]);

// Delete — inside vibeRequest a 204 turns into ['success' => true]
vibeRequest("$VIBE_URL/v1/deals/$dealId", 'DELETE');
```

Full request body formats and response fields are on the pages for specific entities, for example [Deals](./entities/deals.md).

## Error handling

On failure the response body has the form `{ success: false, error: { code, message } }` and the corresponding HTTP status. Example — a request without a key:

```bash
curl -i "$VIBE_URL/v1/deals"
```

```
HTTP/2 401
content-type: application/json; charset=utf-8

{
  "success": false,
  "error": {
    "code": "MISSING_API_KEY",
    "message": "API key required. Pass via X-Api-Key header."
  }
}
```

The full reference of codes and recommended actions — [Error codes](./errors.md).

## Related sections

- [Quickstart](./quickstart.md) — the first request in 2 minutes
- [Keys and authorization](./keys-auth.md) — key types, scopes, limits, session tokens
- [Entity API](./entity-api.md) — CRUD, pagination, field selection for all entities
- [Filtering and search](./filtering.md) — three filter syntaxes with examples
- [Batch API](./batch.md) — combining requests, a 50× speedup
- [Optimization](./optimization.md) — patterns for dashboards and bulk operations
- [Error codes](./errors.md) — error reference
- [MCP for AI](./mcp.md) — integration with AI tools
