> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.talkif.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.talkif.ai/_mcp/server.

# Quickstart

> Build a voice agent, talk to it in your browser, publish it, put it on a phone number, and make a real call — in about ten minutes.

By the end of this page you will have talked to an agent you designed, twice: once from your browser, once over an actual phone line. Along the way you'll meet every object in Talkif — flow, number, call, credits — in the order you'll use them every day.

The dashboard shows the same five steps as a checklist on your home screen until you've completed them. This page is the long version, with the *why* for each one.

## Before you start

* **An account.** Sign up at [app.talkif.ai](https://app.talkif.ai) with email + password or **Continue with Google**. Email sign-ups get a verification link (valid 24 hours) and an SMS code for the phone number you register. Both must be confirmed before the account can place calls.
* **Credits.** New accounts receive a **\$5.00 welcome credit**. It's enough to build, test in the browser, and take a few short calls.

> **The welcome credit can't buy a phone number**
>
> Welcome credit is a *bonus* balance. Bonus and promotional balances pay for calls but are never used for phone-number purchases or monthly rental. To buy a number in step 4 you'll add a payment method and top up first — the number itself costs a dollar or two a month, and the top-up minimum is shown on the Billing page. Everything before step 4 works on the welcome credit alone.

## What you're going to build

A single-agent support line: it greets the caller, asks what they need, answers from the instructions you give it, and ends the call politely. One agent, one flow — the same structure scales to multi-agent flows later without redoing anything here.

#### Create a flow and give the agent its instructions

Open **Voice agents** in the sidebar and choose **New agent**. You can start from a template or from an empty canvas; for this walkthrough start empty so you see every piece.

The canvas opens with a **Main Agent** already placed. It's the entry point of every call and the only node that carries the call's persona. Select it and fill in the two prompts:

* **System Prompt** — who the agent is, for the whole call. Keep it to identity, tone, and hard limits:

  **`System Prompt`**

  ```text title="System Prompt"
  You are Mia, the phone assistant for Northwind Dental in Seattle.
  Be warm and brief — this is a phone call, so keep every reply to one or two sentences.
  Never give medical advice. If you don't know something, say so and offer to take a message.
  ```

* **Agent Prompt** — what this agent must accomplish while the conversation is on it:

  **`Agent Prompt`**

  ```text title="Agent Prompt"
  Greet the caller and ask how you can help.
  Answer questions about opening hours (Mon–Fri 8am–5pm), location (1200 Pine St), and parking (free garage on Pine).
  If they want to book or change an appointment, collect their full name and a callback number, confirm both back to them, and say the office will call within one business day.
  When the caller has nothing else, say a natural goodbye and then end the call.
  ```

> **Tip**
>
> Prompts are instructions, not scripts. Tell the agent what to achieve and what to collect; let it choose the words. Scripted line-by-line prompts sound robotic and break the moment a caller says something unexpected. Prompts can also use `{{contact.first_name}}`-style variables that are filled in when the call starts — see [Write prompts](/build/write-prompts).

#### Attach the three providers

An agent can't hear, think, or speak until a provider node is connected for each job. Add an **STT**, an **LLM**, and a **TTS** node to the canvas and connect each one to the Main Agent.

| Node    | Pick for this walkthrough             | Why it matters                                                                                    |
| ------- | ------------------------------------- | ------------------------------------------------------------------------------------------------- |
| **STT** | Deepgram, English                     | Converts the caller's speech to text *while they talk* — accuracy here caps everything downstream |
| **LLM** | Any current OpenAI or Anthropic model | Decides every reply. Faster models feel more natural on the phone than smarter-but-slower ones    |
| **TTS** | ElevenLabs, any voice                 | The agent's voice. Try two voices — it changes how the same prompt lands                          |

The live list of vendors and models is in [Models and voices](/reference/models-and-voices). Publishing is refused if any of the three is missing, so the builder flags it early.

#### Talk to it in your browser

Open the **Flow Tester** from the builder toolbar and click the phone button. Allow microphone access when the browser asks. You're now on a WebRTC call with the *draft* you have open — unsaved edits included — so you can iterate on prompts without publishing.

Say "Hi, what time do you close on Friday?" and then "I'd like to book a cleaning." Watch the transcript, the per-turn latency (**LLM TTFB**) and token counts on the right; those numbers tell you whether a model is fast enough for phone use before a real caller finds out.

> **Note**
>
> Test calls use the same STT/LLM/TTS as a real call and are billed the same way, at the same rates. They need an available balance of at least **\$1.00** to start — the welcome credit covers this.

Adjust the prompts until the conversation feels right, then hang up with the red button.

#### Publish the flow

Click **Publish**. Talkif validates the flow and refuses to publish until it passes. The checks that matter most:

* exactly one Main Agent, and it's the entry node
* the Main Agent has a System Prompt; **every** agent has an Agent Prompt
* an LLM, an STT and a TTS provider are configured, with models the platform currently offers
* every transition points at an agent that exists; agent and function names are unique

Publishing creates an immutable **version**. The draft stays editable; phone numbers and API calls use the published version until you publish again. Versioning, rollback and pinning are explained in [Flows and versions](/concepts/flows-and-versions).

#### Get a phone number

Open **Phone & WhatsApp** in the sidebar and choose **Purchase**. Pick a country, then search by area code or digit pattern. Each result shows its capabilities (voice, SMS) and its monthly price. Confirm to buy; the number is provisioned instantly.

Purchases and rental are paid from your *purchased* balance — add a payment method and top up under **Billing** if you haven't. Numbers renew monthly against the same balance; see [Buy a number](/telephony/buy-a-number) for pricing and what happens if renewal fails.

#### Dashboard

**Phone & WhatsApp → Purchase**, choose country → search → buy.

#### API

The platform's own carrier is a *system provider* — list providers to get its `id`, then search and purchase through it.

### Request

GET [https://api.talkif.ai/api/v1/phone/numbers/twilio/available](https://api.talkif.ai/api/v1/phone/numbers/twilio/available)

```curl
curl -G https://api.talkif.ai/api/v1/phone/numbers/twilio/available \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d countryCode=countryCode \
     -d providerId=providerId
```

```typescript
import { TalkifClient } from "@talkif/sdk";

async function main() {
    const client = new TalkifClient({
        token: "YOUR_TOKEN_HERE",
    });
    await client.phoneNumbers.listAvailableNumbers({
        countryCode: "countryCode",
        providerId: "providerId",
    });
}
main();

```

```python
import requests

url = "https://api.talkif.ai/api/v1/phone/numbers/twilio/available"

querystring = {"countryCode":"countryCode","providerId":"providerId"}

payload = {}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.get(url, json=payload, headers=headers, params=querystring)

print(response.json())
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.talkif.ai/api/v1/phone/numbers/twilio/available?countryCode=countryCode&providerId=providerId"

	payload := strings.NewReader("{}")

	req, _ := http.NewRequest("GET", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.talkif.ai/api/v1/phone/numbers/twilio/available?countryCode=countryCode&providerId=providerId")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.talkif.ai/api/v1/phone/numbers/twilio/available?countryCode=countryCode&providerId=providerId")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.talkif.ai/api/v1/phone/numbers/twilio/available?countryCode=countryCode&providerId=providerId', [
  'body' => '{}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.talkif.ai/api/v1/phone/numbers/twilio/available?countryCode=countryCode&providerId=providerId");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.talkif.ai/api/v1/phone/numbers/twilio/available?countryCode=countryCode&providerId=providerId")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Request

POST [https://api.talkif.ai/api/v1/phone/numbers/twilio/purchase](https://api.talkif.ai/api/v1/phone/numbers/twilio/purchase)

```curl
curl -X POST https://api.talkif.ai/api/v1/phone/numbers/twilio/purchase \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "phoneNumber": "+15551234567",
  "providerId": "550e8400-e29b-41d4-a716-446655440000"
}'
```

```typescript
import { TalkifClient } from "@talkif/sdk";

async function main() {
    const client = new TalkifClient({
        token: "YOUR_TOKEN_HERE",
    });
    await client.phoneNumbers.purchasePhoneNumber({
        phoneNumber: "+15551234567",
        providerId: "550e8400-e29b-41d4-a716-446655440000",
    });
}
main();

```

```python
import requests

url = "https://api.talkif.ai/api/v1/phone/numbers/twilio/purchase"

payload = {
    "phoneNumber": "+15551234567",
    "providerId": "550e8400-e29b-41d4-a716-446655440000"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.talkif.ai/api/v1/phone/numbers/twilio/purchase"

	payload := strings.NewReader("{\n  \"phoneNumber\": \"+15551234567\",\n  \"providerId\": \"550e8400-e29b-41d4-a716-446655440000\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.talkif.ai/api/v1/phone/numbers/twilio/purchase")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"phoneNumber\": \"+15551234567\",\n  \"providerId\": \"550e8400-e29b-41d4-a716-446655440000\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.talkif.ai/api/v1/phone/numbers/twilio/purchase")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"phoneNumber\": \"+15551234567\",\n  \"providerId\": \"550e8400-e29b-41d4-a716-446655440000\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.talkif.ai/api/v1/phone/numbers/twilio/purchase', [
  'body' => '{
  "phoneNumber": "+15551234567",
  "providerId": "550e8400-e29b-41d4-a716-446655440000"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.talkif.ai/api/v1/phone/numbers/twilio/purchase");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"phoneNumber\": \"+15551234567\",\n  \"providerId\": \"550e8400-e29b-41d4-a716-446655440000\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "phoneNumber": "+15551234567",
  "providerId": "550e8400-e29b-41d4-a716-446655440000"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.talkif.ai/api/v1/phone/numbers/twilio/purchase")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

Already have numbers with a SIP carrier or a WhatsApp Business account? Skip the purchase and connect them instead — [Bring your own SIP trunk](/telephony/bring-your-own-sip-trunk), [WhatsApp Business Calling](/telephony/whatsapp-business-calling).

#### Connect the flow to the number

Open the number you just bought and connect your published flow to it. From this moment, anyone who dials the number is answered by the agent — this is what makes a number *inbound-capable*.

#### Dashboard

**Phone & WhatsApp →** on the number's row, pick a published flow in the agent picker. Leave the version on *latest published* unless you have a reason to pin.

#### API

### Request

PATCH [https://api.talkif.ai/api/v1/phone/numbers/\{phoneNumberId}/connect-flow](https://api.talkif.ai/api/v1/phone/numbers/\{phoneNumberId}/connect-flow)

```curl
curl -X PATCH https://api.talkif.ai/api/v1/phone/numbers/phoneNumberId/connect-flow \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "flowId": "550e8400-e29b-41d4-a716-446655440000"
}'
```

```typescript
import { TalkifClient } from "@talkif/sdk";

async function main() {
    const client = new TalkifClient({
        token: "YOUR_TOKEN_HERE",
    });
    await client.phoneNumbers.connectFlow({
        phoneNumberId: "phoneNumberId",
        flowId: "550e8400-e29b-41d4-a716-446655440000",
    });
}
main();

```

```python
import requests

url = "https://api.talkif.ai/api/v1/phone/numbers/phoneNumberId/connect-flow"

payload = { "flowId": "550e8400-e29b-41d4-a716-446655440000" }
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.patch(url, json=payload, headers=headers)

print(response.json())
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.talkif.ai/api/v1/phone/numbers/phoneNumberId/connect-flow"

	payload := strings.NewReader("{\n  \"flowId\": \"550e8400-e29b-41d4-a716-446655440000\"\n}")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.talkif.ai/api/v1/phone/numbers/phoneNumberId/connect-flow")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"flowId\": \"550e8400-e29b-41d4-a716-446655440000\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.patch("https://api.talkif.ai/api/v1/phone/numbers/phoneNumberId/connect-flow")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"flowId\": \"550e8400-e29b-41d4-a716-446655440000\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('PATCH', 'https://api.talkif.ai/api/v1/phone/numbers/phoneNumberId/connect-flow', [
  'body' => '{
  "flowId": "550e8400-e29b-41d4-a716-446655440000"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.talkif.ai/api/v1/phone/numbers/phoneNumberId/connect-flow");
var request = new RestRequest(Method.PATCH);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"flowId\": \"550e8400-e29b-41d4-a716-446655440000\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = ["flowId": "550e8400-e29b-41d4-a716-446655440000"] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.talkif.ai/api/v1/phone/numbers/phoneNumberId/connect-flow")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

A number is connected to at most one flow. If someone dials a number with no flow connected, Talkif answers busy and records a *failed* call in your history with the reason "no connected flow" — so a misconfigured number is visible, not silent. The same busy response is returned when your balance is below the \$1.00 minimum.

#### Make a real call

Two ways, and it's worth doing both:

**Inbound** — dial the number from your own phone. This is the path your customers will take.

**Outbound** — have the agent call *you*. From the dashboard use **Start a call** on the home screen (or **Calls**), pick the flow, the number to call from, and your mobile as the destination. From the API, the same thing is one request:

#### Dashboard

Home screen **→ Start a call →** flow, from-number, destination **→ Call**.

#### API

`fromNumber` must be a number your account owns; `providerId` is the provider that number belongs to (returned when you list numbers). You'll need an [API key](/integrate/authentication) first.

### Request

POST [https://api.talkif.ai/api/v1/calls](https://api.talkif.ai/api/v1/calls)

```curl
curl -X POST https://api.talkif.ai/api/v1/calls \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "flowId": "550e8400-e29b-41d4-a716-446655440000",
  "fromNumber": "+15559876543",
  "providerId": "550e8400-e29b-41d4-a716-446655440000",
  "toNumber": "+15551234567"
}'
```

```typescript
import { TalkifClient } from "@talkif/sdk";

async function main() {
    const client = new TalkifClient({
        token: "YOUR_TOKEN_HERE",
    });
    await client.calls.makeCall({
        flowId: "550e8400-e29b-41d4-a716-446655440000",
        fromNumber: "+15559876543",
        providerId: "550e8400-e29b-41d4-a716-446655440000",
        toNumber: "+15551234567",
    });
}
main();

```

```python
import requests

url = "https://api.talkif.ai/api/v1/calls"

payload = {
    "flowId": "550e8400-e29b-41d4-a716-446655440000",
    "fromNumber": "+15559876543",
    "providerId": "550e8400-e29b-41d4-a716-446655440000",
    "toNumber": "+15551234567"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.talkif.ai/api/v1/calls"

	payload := strings.NewReader("{\n  \"flowId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"fromNumber\": \"+15559876543\",\n  \"providerId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"toNumber\": \"+15551234567\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.talkif.ai/api/v1/calls")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"flowId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"fromNumber\": \"+15559876543\",\n  \"providerId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"toNumber\": \"+15551234567\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.talkif.ai/api/v1/calls")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"flowId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"fromNumber\": \"+15559876543\",\n  \"providerId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"toNumber\": \"+15551234567\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.talkif.ai/api/v1/calls', [
  'body' => '{
  "flowId": "550e8400-e29b-41d4-a716-446655440000",
  "fromNumber": "+15559876543",
  "providerId": "550e8400-e29b-41d4-a716-446655440000",
  "toNumber": "+15551234567"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.talkif.ai/api/v1/calls");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"flowId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"fromNumber\": \"+15559876543\",\n  \"providerId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"toNumber\": \"+15551234567\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "flowId": "550e8400-e29b-41d4-a716-446655440000",
  "fromNumber": "+15559876543",
  "providerId": "550e8400-e29b-41d4-a716-446655440000",
  "toNumber": "+15551234567"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.talkif.ai/api/v1/calls")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

A `201` means the call is being placed now. A `202` means your account is at its concurrent-call limit and the call is queued; the response carries your queue position and it'll be placed automatically.

#### Read the result

Open **Calls** and select the call. You get three things for every call, test or real:

* the **transcript**, turn by turn, with who spoke and when
* the **recording**, playable in place
* the **cost breakdown** — telephony seconds, STT seconds, LLM tokens, TTS characters — each priced, summed to the amount taken from your balance

Costs are where model choice becomes visible: swap the LLM or TTS in the draft, run the same conversation in the Flow Tester, and compare. [Cost breakdown](/account/cost-breakdown) explains every line.

## You now have

A published flow, a live number connected to it, and at least one completed call with a transcript and a cost. Everything else in Talkif is a way to do this at scale or with more precision:

#### [How a call works](/getting-started/how-a-call-works)

What happened between "Call" and the transcript — statuses, queueing, end reasons, billing.

#### [Design a conversation](/build/design-a-conversation)

Split the job across agents, add transitions, and hand off cleanly.

#### [Call your backend](/build/call-your-backend)

Let the agent look things up or take action in your systems mid-call.

#### [Campaigns](/calls/campaigns)

Call a list of contacts with one flow, with pacing, retries and analytics.