---
title: "Use Case 4: Mock CRM lookup"
description: "HTTP request to a mock API, save JSON to a variable, branch ok / not_ok, and Instance Settings for the token."
---

# Use Case 4: Mock CRM lookup

**Level:** basic · **Modules:** Scenario Builder, Instance Settings · **Action:** `action_send_request`

> **Before you start:** read [Mock vs production API](/en/learn/implementer/training/concepts.md#mock-vs-production-api) and [Branching: Router / switch](/en/learn/implementer/training/concepts.md#router-branching).

## Required permissions and access

| What you need | Where in the menu | Why |
|---------------|-------------------|-----|
| **Scenario Builder** | Menu → Scenario Builder | WaitForInput, send_request, Router |
| **Instance Settings** | Settings → Instance Settings | Key `training_api_token` for Bearer |

If a section is missing from the menu — check your account permissions or contact ConnectiveOne support.

## Business context

The bot checks a “customer” via an external API. In training use a **mock URL** — you don’t need a real CRM. This is the pattern for production integrations: request → variable → branch by result.

## Expected result

- GET to a mock URL → JSON in a variable (e.g. `crm_data`).
- Branching: customer found / not found / API error.
- Edge **`not_ok`** / **`error`** → message to the customer (not silence).
- The token is stored in Instance Settings, not in scenario code.

## Flow architecture

```text
WaitForInput (phone) → action_send_request (GET mock)
   ├─ ok → Router → “Customer: {{crm_data.name}}”
   ├─ not_ok → “Temporarily unavailable” (+ operator optionally)
   └─ (in ok) field check → “New customer”
```

## Step-by-step implementation

### Step 0. Instance Settings

Settings → **Instance Settings** → key:

| Key | Value (training) | Why |
|-----|------------------|-----|
| `training_api_token` | any string | Simulate a CRM Bearer token |

In the scenario: `{{instance_settings.training_api_token}}` — the secret is not in Action Jail code.

---

### Step 1. WaitForInput — phone

| Parameter | Value | Why |
|-----------|-------|-----|
| messageText | “Enter phone number” | |
| outputVariable | `phone_raw` | For URL or body ([Use Case 5](/en/learn/implementer/training/basic/use-case-05-action-jail-phone.md) normalizes) |
| Validation → type | `phone` *(or `none` on the first pass)* | Less noise in the mock request |

**Edges:** successful input → `action_send_request`.

---

### Step 2. Action — `action_send_request`

**What the action does:** HTTP request to an external URL; on success saves the response body in constants; returns event `ok` / `not_ok`.

| node_params | Value (mock) | Why |
|-------------|--------------|-----|
| `url` | `https://jsonplaceholder.typicode.com/users/1` | Stable JSON without registration |
| `method` | `GET` | Lookup without body |
| `headers` | `{"Content-Type": "application/json"}` | Explicit type |
| `bearer_auth.token` | `{{instance_settings.training_api_token}}` | Auth pattern via Instance Settings |
| `save_responce` | `crm_data` | **Variable name** with the response → `{{crm_data.name}}`, `{{crm_data.email}}` *(Engine field has a historical typo `responce`)* |

Alternative: `response_mapping` — map individual JSON fields to named constants.

**Edges:**

| Edge | When | Where | Why |
|------|------|-------|-----|
| **`ok`** | HTTP success, `response.data` present | Router / MessageKeyboard “Found: {{crm_data.name}}” | Happy path |
| **`not_ok`** | Network, 4xx/5xx, empty response | “Service temporarily unavailable” | **Required** — otherwise silence |
| `error` *(if present in Inspector)* | Action failure | Same or escalate | Failsafe |

Error check: temporarily use URL `https://jsonplaceholder.typicode.com/404` → must be **`not_ok`**.

---

### Step 3. Router — interpret the response

**Node:** Router (`action_router`), mode `first_match`.

| Rule | Why |
|------|-----|
| `{{crm_data.name}}` **not_empty** | “Customer found: {{crm_data.name}}” |
| **Default branch** | “New customer — no data in CRM” |

**Alternative:** Action **`switch`** with the same conditions.

> Legacy **`if_else`** in older scenarios — same logic; in training use Router.

**Edges:** each Router branch → MessageKeyboard.

---

### Step 4. Test in Runs

1. Runs → after `ok` constants contain the `crm_data` object.
2. Broken URL → **`not_ok`** + your message.
3. Record in the [results file](/en/learn/implementer/training/results-template.md): which edge fired and which variables are in the trace.

## Acceptance criteria (self-check Runs)

- [ ] Happy path: after `ok` the trace has `crm_data` with JSON fields.
- [ ] URL 404 or broken → edge **`not_ok`**, customer sees a message.
- [ ] Edges **`ok`** and **`not_ok`** are connected on the canvas.
- [ ] Token comes from `{{instance_settings.training_api_token}}`.
- [ ] Router (or `switch`) correctly distinguishes “found” and “new customer.”
- [ ] *(Optional)* The same CRM flow rebuilt via Instance Agent and verified in Runs ([rule](/en/learn/implementer/training/prerequisites.md#instance-agent-rule)).

## Common mistakes

| Symptom | Cause | What to do |
|---------|-------|------------|
| Bot “goes silent” after the request | No **not_ok** edge | Connect not_ok → MessageKeyboard |
| `{{crm_data.name}}` literally | Wrong `save_responce` | Check the variable name in node_params |
| Always not_ok | Wrong URL or network | Check the URL in a browser |
| 401 in the trace | Bearer not passed | Check Instance Settings and `bearer_auth.token` |

## Related documentation

- [Mock vs production API](/en/learn/implementer/training/concepts.md#mock-vs-production-api)
- [send_request in actions-reference](/en/actionjail/reference/actions-reference.md)
- [Action Jail — integrations](/en/actionjail/how-to/integrator-embed-actions.md)
- [FAQ — CRM / mock](/en/learn/implementer/training/reference/faq.md)
- Next: [Use Case 5 — phone normalization](/en/learn/implementer/training/basic/use-case-05-action-jail-phone.md)
