# createTestClock

createTestClock creates an isolated, disposable simulation scenario.

Last updated October 2, 2026

## Reference

Authentication: **bearer token required** (see [how to get a token](https://financesavi.com/developers/authentication#get-a-token))

```
createTestClock creates an isolated, disposable simulation scenario.

Authentication: required. The clock is owned by the authenticated identity and
expires after seven days. Its data stays outside the ordinary account, transaction,
goal, and bank-connection collections. Transactions and goal contributions
appear as the clock advances; no bank request is sent.

Parameters:
- frozenTime (RFC 3339 timestamp, optional): initial simulated time; defaults to now.
- transactions (array, optional): up to 100 entries. Each needs an RFC 3339 at
  between frozenTime and maxTime, a 1–120 character name, a positive amount up
  to 1,000,000,000, a three-letter uppercase currency code, and type INCOME or
  EXPENSE. Category is optional and limited to 80 characters.
- goals (array, optional): up to 25 goals with a 1–120 character name and a
  positive targetAmount up to 1,000,000,000. initialAmount defaults to zero
  and cannot exceed targetAmount. Each goal accepts up to 100 positive
  contributions, each up to 1,000,000,000, scheduled between frozenTime and
  maxTime.

Time range: frozenTime must be within ten years of now. maxTime is ten years
after frozenTime. Event times outside that range are rejected.

Returns: clock ID, frozen time, visible transactions, pending transactions, and
goal progress with posted and upcoming contributions.
```

## createTestClock request

POST https://api-v2.financesavi.com/rpc · Bearer token required

### cURL

```bash
curl -sS --max-time 30 -X POST 'https://api-v2.financesavi.com/rpc' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $SAVI_TOKEN" \
  -d '{"jsonrpc":"2.0","method":"createTestClock","params":{"frozenTime":"2026-10-02T18:24:43.985Z","transactions":[{"at":"2026-10-02T19:24:43.985Z","name":"Simulated payroll","amount":2500,"currency":"CAD","type":"INCOME"}],"goals":[{"name":"Emergency fund","targetAmount":1000,"initialAmount":100,"contributions":[{"at":"2026-10-02T19:24:43.985Z","amount":150},{"at":"2026-10-02T20:24:43.985Z","amount":250}]}]},"id":1}'
```

### JavaScript

```javascript
const response = await fetch("https://api-v2.financesavi.com/rpc", {
  method: 'POST',
  signal: AbortSignal.timeout(30_000),
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.SAVI_TOKEN}`,
  },
  body: JSON.stringify({
      "jsonrpc": "2.0",
      "method": "createTestClock",
      "params": {
        "frozenTime": "2026-10-02T18:24:43.985Z",
        "transactions": [
          {
            "at": "2026-10-02T19:24:43.985Z",
            "name": "Simulated payroll",
            "amount": 2500,
            "currency": "CAD",
            "type": "INCOME"
          }
        ],
        "goals": [
          {
            "name": "Emergency fund",
            "targetAmount": 1000,
            "initialAmount": 100,
            "contributions": [
              {
                "at": "2026-10-02T19:24:43.985Z",
                "amount": 150
              },
              {
                "at": "2026-10-02T20:24:43.985Z",
                "amount": 250
              }
            ]
          }
        ]
      },
      "id": 1
    }),
});
console.log(await response.json());
```

### TypeScript

```typescript
const response = await fetch("https://api-v2.financesavi.com/rpc", {
  method: 'POST',
  signal: AbortSignal.timeout(30_000),
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.SAVI_TOKEN}`,
  },
  body: JSON.stringify({
      "jsonrpc": "2.0",
      "method": "createTestClock",
      "params": {
        "frozenTime": "2026-10-02T18:24:43.985Z",
        "transactions": [
          {
            "at": "2026-10-02T19:24:43.985Z",
            "name": "Simulated payroll",
            "amount": 2500,
            "currency": "CAD",
            "type": "INCOME"
          }
        ],
        "goals": [
          {
            "name": "Emergency fund",
            "targetAmount": 1000,
            "initialAmount": 100,
            "contributions": [
              {
                "at": "2026-10-02T19:24:43.985Z",
                "amount": 150
              },
              {
                "at": "2026-10-02T20:24:43.985Z",
                "amount": 250
              }
            ]
          }
        ]
      },
      "id": 1
    }),
});
console.log(await response.json());
```

### Python

```python
import os
import requests

response = requests.post(
    "https://api-v2.financesavi.com/rpc",
    headers={"Authorization": f"Bearer {os.environ['SAVI_TOKEN']}"},
    json={
        "jsonrpc": "2.0",
        "method": "createTestClock",
        "params": {
            "frozenTime": "2026-10-02T18:24:43.985Z",
            "transactions": [
                {
                    "at": "2026-10-02T19:24:43.985Z",
                    "name": "Simulated payroll",
                    "amount": 2500,
                    "currency": "CAD",
                    "type": "INCOME",
                },
            ],
            "goals": [
                {
                    "name": "Emergency fund",
                    "targetAmount": 1000,
                    "initialAmount": 100,
                    "contributions": [
                        {
                            "at": "2026-10-02T19:24:43.985Z",
                            "amount": 150,
                        },
                        {
                            "at": "2026-10-02T20:24:43.985Z",
                            "amount": 250,
                        },
                    ],
                },
            ],
        },
        "id": 1,
    },
    timeout=30,
)
print(response.json())
```

### Go

```go
package main

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

func main() {
  payload := strings.NewReader("{\"jsonrpc\":\"2.0\",\"method\":\"createTestClock\",\"params\":{\"frozenTime\":\"2026-10-02T18:24:43.985Z\",\"transactions\":[{\"at\":\"2026-10-02T19:24:43.985Z\",\"name\":\"Simulated payroll\",\"amount\":2500,\"currency\":\"CAD\",\"type\":\"INCOME\"}],\"goals\":[{\"name\":\"Emergency fund\",\"targetAmount\":1000,\"initialAmount\":100,\"contributions\":[{\"at\":\"2026-10-02T19:24:43.985Z\",\"amount\":150},{\"at\":\"2026-10-02T20:24:43.985Z\",\"amount\":250}]}]},\"id\":1}")
  req, err := http.NewRequest("POST", "https://api-v2.financesavi.com/rpc", payload)
  if err != nil { panic(err) }
  req.Header.Set("Content-Type", "application/json")
  req.Header.Set("Authorization", "Bearer " + os.Getenv("SAVI_TOKEN"))
  client := &http.Client{Timeout: 30 * time.Second}
  res, err := client.Do(req)
  if err != nil { panic(err) }
  defer res.Body.Close()
  body, err := io.ReadAll(res.Body)
  if err != nil { panic(err) }
  fmt.Println(res.Status, string(body))
}
```

### C#

```csharp
// .NET 6 or later
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("SAVI_TOKEN"));
using var content = new StringContent(@"{""jsonrpc"":""2.0"",""method"":""createTestClock"",""params"":{""frozenTime"":""2026-10-02T18:24:43.985Z"",""transactions"":[{""at"":""2026-10-02T19:24:43.985Z"",""name"":""Simulated payroll"",""amount"":2500,""currency"":""CAD"",""type"":""INCOME""}],""goals"":[{""name"":""Emergency fund"",""targetAmount"":1000,""initialAmount"":100,""contributions"":[{""at"":""2026-10-02T19:24:43.985Z"",""amount"":150},{""at"":""2026-10-02T20:24:43.985Z"",""amount"":250}]}]},""id"":1}", Encoding.UTF8, "application/json");
using var response = await client.PostAsync(@"https://api-v2.financesavi.com/rpc", content);
Console.WriteLine($"HTTP {(int)response.StatusCode}");
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

### Java

```java
// Java 11+: save as SaviExample.java, then run java SaviExample.java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

class SaviExample {
  public static void main(String[] args) throws Exception {
    var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
    var builder = HttpRequest.newBuilder(URI.create("https://api-v2.financesavi.com/rpc"))
        .timeout(Duration.ofSeconds(30))
        .header("Content-Type", "application/json");
    builder.header("Authorization", "Bearer " + System.getenv("SAVI_TOKEN"));
    var request = builder.POST(HttpRequest.BodyPublishers.ofString("{\"jsonrpc\":\"2.0\",\"method\":\"createTestClock\",\"params\":{\"frozenTime\":\"2026-10-02T18:24:43.985Z\",\"transactions\":[{\"at\":\"2026-10-02T19:24:43.985Z\",\"name\":\"Simulated payroll\",\"amount\":2500,\"currency\":\"CAD\",\"type\":\"INCOME\"}],\"goals\":[{\"name\":\"Emergency fund\",\"targetAmount\":1000,\"initialAmount\":100,\"contributions\":[{\"at\":\"2026-10-02T19:24:43.985Z\",\"amount\":150},{\"at\":\"2026-10-02T20:24:43.985Z\",\"amount\":250}]}]},\"id\":1}")).build();
    var response = client.send(request, HttpResponse.BodyHandlers.ofString());
    System.out.println("HTTP " + response.statusCode());
    System.out.println(response.body());
  }
}
```

### PHP

```php
<?php
// Requires the PHP cURL extension.
$curl = curl_init('https://api-v2.financesavi.com/rpc');
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 30,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'Authorization: Bearer ' . getenv('SAVI_TOKEN'),
    ],
    CURLOPT_POSTFIELDS => '{"jsonrpc":"2.0","method":"createTestClock","params":{"frozenTime":"2026-10-02T18:24:43.985Z","transactions":[{"at":"2026-10-02T19:24:43.985Z","name":"Simulated payroll","amount":2500,"currency":"CAD","type":"INCOME"}],"goals":[{"name":"Emergency fund","targetAmount":1000,"initialAmount":100,"contributions":[{"at":"2026-10-02T19:24:43.985Z","amount":150},{"at":"2026-10-02T20:24:43.985Z","amount":250}]}]},"id":1}',
]);
$body = curl_exec($curl);
if ($body === false) {
    throw new RuntimeException(curl_error($curl));
}
echo "HTTP " . curl_getinfo($curl, CURLINFO_HTTP_CODE) . PHP_EOL;
echo $body . PHP_EOL;
curl_close($curl);
```

### Ruby

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

uri = URI('https://api-v2.financesavi.com/rpc')
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer ' + ENV.fetch('SAVI_TOKEN')
request.body = '{"jsonrpc":"2.0","method":"createTestClock","params":{"frozenTime":"2026-10-02T18:24:43.985Z","transactions":[{"at":"2026-10-02T19:24:43.985Z","name":"Simulated payroll","amount":2500,"currency":"CAD","type":"INCOME"}],"goals":[{"name":"Emergency fund","targetAmount":1000,"initialAmount":100,"contributions":[{"at":"2026-10-02T19:24:43.985Z","amount":150},{"at":"2026-10-02T20:24:43.985Z","amount":250}]}]},"id":1}'
response = Net::HTTP.start(uri.host, uri.port,
  use_ssl: uri.scheme == 'https', open_timeout: 30, read_timeout: 30, write_timeout: 30
) { |http| http.request(request) }
puts "HTTP #{response.code}"
puts response.body
```

### Swift

```swift
// Swift 5.5+: save as SaviExample.swift, then run swift SaviExample.swift
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

var request = URLRequest(url: URL(string: "https://api-v2.financesavi.com/rpc")!)
request.httpMethod = "POST"
request.timeoutInterval = 30
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
guard let token = ProcessInfo.processInfo.environment["SAVI_TOKEN"] else { fatalError("Set SAVI_TOKEN first") }
request.setValue("Bearer " + token, forHTTPHeaderField: "Authorization")
request.httpBody = Data("{\"jsonrpc\":\"2.0\",\"method\":\"createTestClock\",\"params\":{\"frozenTime\":\"2026-10-02T18:24:43.985Z\",\"transactions\":[{\"at\":\"2026-10-02T19:24:43.985Z\",\"name\":\"Simulated payroll\",\"amount\":2500,\"currency\":\"CAD\",\"type\":\"INCOME\"}],\"goals\":[{\"name\":\"Emergency fund\",\"targetAmount\":1000,\"initialAmount\":100,\"contributions\":[{\"at\":\"2026-10-02T19:24:43.985Z\",\"amount\":150},{\"at\":\"2026-10-02T20:24:43.985Z\",\"amount\":250}]}]},\"id\":1}".utf8)
let configuration = URLSessionConfiguration.ephemeral
configuration.timeoutIntervalForResource = 30
let session = URLSession(configuration: configuration)
let (data, response) = try await session.data(for: request)
if let http = response as? HTTPURLResponse { print("HTTP", http.statusCode) }
print(String(decoding: data, as: UTF8.self))
```

### Local API capture (scrubbed) (HTTP 200)

```json
{
  "jsonrpc": "2.0",
  "result": {
    "clock": {
      "id": "tc_example",
      "status": "READY",
      "frozenTime": "2026-10-02T18:24:43.985Z",
      "maxTime": "2036-10-02T18:24:43.985Z",
      "expiresAt": "2026-10-09T18:25:43.990284Z",
      "transactions": [],
      "pendingTransactions": [
        {
          "id": "txn_001",
          "at": "2026-10-02T19:24:43.985Z",
          "name": "Simulated payroll",
          "amount": 2500,
          "currency": "CAD",
          "type": "INCOME",
          "source": "SIMULATED_TEST_CLOCK"
        }
      ],
      "goals": [
        {
          "id": "goal_001",
          "name": "Emergency fund",
          "targetAmount": 1000,
          "currentAmount": 100,
          "remainingAmount": 900,
          "progressPercent": 10,
          "contributions": [],
          "upcomingContributions": [
            {
              "id": "contribution_001",
              "at": "2026-10-02T19:24:43.985Z",
              "amount": 150
            },
            {
              "id": "contribution_002",
              "at": "2026-10-02T20:24:43.985Z",
              "amount": 250
            }
          ]
        }
      ]
    }
  },
  "id": 1
}
```

Source: https://financesavi.com/developers/api/createTestClock
