REST quickstart
This guide takes you from no access to a created job. You need to be an owner or admin of a FieldTask organization with API access enabled.
1. Create an API token
- In FieldTask, open Settings → API connections. If you see Request API access, request it and wait for FieldTask to enable it.
- Choose Create API token and name it, for example "Quickstart".
- Choose Custom and select Create jobs. Read jobs and Read customers are selected automatically.
- Create the token and copy it. It's shown only once; choose I've saved it when you have it.
Set it and your API origin (shown in Settings → API connections) in your shell:
export FIELDTASK_API_ORIGIN="https://api.fieldtask.example" # use your own origin
read -r -s -p "FieldTask API token: " FIELDTASK_API_TOKEN && export FIELDTASK_API_TOKEN
2. Find a customer
- cURL
- Python
- Node.js
- Ruby
- PHP
- Java
- Go
- .NET
- Rust
curl "$FIELDTASK_API_ORIGIN/public/v1/customers?page_size=5" \
--header "Authorization: Bearer $FIELDTASK_API_TOKEN"
import os
import requests # pip install requests
response = requests.get(
f"{os.environ['FIELDTASK_API_ORIGIN']}/public/v1/customers",
params={"page_size": 5},
headers={"Authorization": f"Bearer {os.environ['FIELDTASK_API_TOKEN']}"},
timeout=30,
)
print(response.status_code, response.text)
// Node.js 18 or later.
const response = await fetch(`${process.env.FIELDTASK_API_ORIGIN}/public/v1/customers?page_size=5`, {
headers: { Authorization: `Bearer ${process.env.FIELDTASK_API_TOKEN}` },
});
console.log(response.status, await response.text());
require "net/http"
uri = URI("#{ENV.fetch("FIELDTASK_API_ORIGIN")}/public/v1/customers?page_size=5")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("FIELDTASK_API_TOKEN")}"
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(request) }
puts response.code, response.body
<?php
// PHP 8.1 or later with the curl extension.
$curl = curl_init(getenv('FIELDTASK_API_ORIGIN') . '/public/v1/customers?page_size=5');
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('FIELDTASK_API_TOKEN')],
]);
$body = curl_exec($curl);
echo curl_getinfo($curl, CURLINFO_RESPONSE_CODE), PHP_EOL, $body, PHP_EOL;
// Java 17 or later. Run with: java ListCustomers.java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class ListCustomers {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(
URI.create(System.getenv("FIELDTASK_API_ORIGIN") + "/public/v1/customers?page_size=5"))
.header("Authorization", "Bearer " + System.getenv("FIELDTASK_API_TOKEN"))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + "\n" + response.body());
}
}
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest(http.MethodGet, os.Getenv("FIELDTASK_API_ORIGIN")+"/public/v1/customers?page_size=5", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("FIELDTASK_API_TOKEN"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(body))
}
// .NET 8: in a new console project (dotnet new console), replace Program.cs with this.
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {Environment.GetEnvironmentVariable("FIELDTASK_API_TOKEN")}");
var response = await client.GetAsync($"{Environment.GetEnvironmentVariable("FIELDTASK_API_ORIGIN")}/public/v1/customers?page_size=5");
Console.WriteLine($"{(int)response.StatusCode}\n{await response.Content.ReadAsStringAsync()}");
// Cargo.toml: reqwest = { version = "0.12", default-features = false, features = ["blocking", "rustls-tls"] }
use std::env;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let response = reqwest::blocking::Client::new()
.get(format!("{}/public/v1/customers?page_size=5", env::var("FIELDTASK_API_ORIGIN")?))
.bearer_auth(env::var("FIELDTASK_API_TOKEN")?)
.send()?;
println!("{}\n{}", response.status(), response.text()?);
Ok(())
}
You get up to five customers. Copy the id of the one the job is for:
{
"data": [
{ "id": "11111111-1111-4111-8111-111111111111", "name": "Example Property Services" }
],
"page": { "has_more": true, "next_cursor": "EXAMPLE_OPAQUE_CURSOR", "next_page_url": "https://api.fieldtask.example/public/v1/customers?page_size=5&cursor=EXAMPLE_OPAQUE_CURSOR" }
}
Customers have more fields than shown here; see List customers. If you have no customers yet, create one with Create a customer, which also needs Create customers.
3. Create a job
Generate an idempotency key and keep it in your shell, so you can retry safely if the request times out:
export IDEMPOTENCY_KEY=$(python3 -c 'import uuid; print(uuid.uuid4())')
Then create an unscheduled job:
- cURL
- Python
- Node.js
- Ruby
- PHP
- Java
- Go
- .NET
- Rust
curl --include --request POST "$FIELDTASK_API_ORIGIN/public/v1/jobs" \
--header "Authorization: Bearer $FIELDTASK_API_TOKEN" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: $IDEMPOTENCY_KEY" \
--data '{
"name": "Quickstart test job",
"customer_id": "11111111-1111-4111-8111-111111111111"
}'
import os
import requests # pip install requests
response = requests.post(
f"{os.environ['FIELDTASK_API_ORIGIN']}/public/v1/jobs",
json={"name": "Quickstart test job", "customer_id": "11111111-1111-4111-8111-111111111111"},
headers={
"Authorization": f"Bearer {os.environ['FIELDTASK_API_TOKEN']}",
"Idempotency-Key": os.environ["IDEMPOTENCY_KEY"],
},
timeout=30,
)
print(response.status_code, "replayed:", response.headers.get("Idempotency-Replayed"))
print(response.text)
// Node.js 18 or later.
const response = await fetch(`${process.env.FIELDTASK_API_ORIGIN}/public/v1/jobs`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.FIELDTASK_API_TOKEN}`,
'Content-Type': 'application/json',
'Idempotency-Key': process.env.IDEMPOTENCY_KEY,
},
body: JSON.stringify({ name: 'Quickstart test job', customer_id: '11111111-1111-4111-8111-111111111111' }),
});
console.log(response.status, 'replayed:', response.headers.get('Idempotency-Replayed'));
console.log(await response.text());
require "json"
require "net/http"
uri = URI("#{ENV.fetch("FIELDTASK_API_ORIGIN")}/public/v1/jobs")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("FIELDTASK_API_TOKEN")}"
request["Content-Type"] = "application/json"
request["Idempotency-Key"] = ENV.fetch("IDEMPOTENCY_KEY")
request.body = JSON.generate(name: "Quickstart test job", customer_id: "11111111-1111-4111-8111-111111111111")
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(request) }
puts "#{response.code} replayed: #{response["Idempotency-Replayed"]}", response.body
<?php
// PHP 8.1 or later with the curl extension.
$curl = curl_init(getenv('FIELDTASK_API_ORIGIN') . '/public/v1/jobs');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['name' => 'Quickstart test job', 'customer_id' => '11111111-1111-4111-8111-111111111111']),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true, // print the response headers too, including Idempotency-Replayed
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('FIELDTASK_API_TOKEN'),
'Content-Type: application/json',
'Idempotency-Key: ' . getenv('IDEMPOTENCY_KEY'),
],
]);
echo curl_exec($curl), PHP_EOL;
// Java 17 or later. Run with: java CreateJob.java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class CreateJob {
public static void main(String[] args) throws Exception {
String job = """
{"name": "Quickstart test job", "customer_id": "11111111-1111-4111-8111-111111111111"}""";
HttpRequest request = HttpRequest.newBuilder(URI.create(System.getenv("FIELDTASK_API_ORIGIN") + "/public/v1/jobs"))
.header("Authorization", "Bearer " + System.getenv("FIELDTASK_API_TOKEN"))
.header("Content-Type", "application/json")
.header("Idempotency-Key", System.getenv("IDEMPOTENCY_KEY"))
.POST(HttpRequest.BodyPublishers.ofString(job))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " replayed: "
+ response.headers().firstValue("Idempotency-Replayed").orElse("") + "\n" + response.body());
}
}
package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
job := `{"name": "Quickstart test job", "customer_id": "11111111-1111-4111-8111-111111111111"}`
req, _ := http.NewRequest(http.MethodPost, os.Getenv("FIELDTASK_API_ORIGIN")+"/public/v1/jobs", strings.NewReader(job))
req.Header.Set("Authorization", "Bearer "+os.Getenv("FIELDTASK_API_TOKEN"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", os.Getenv("IDEMPOTENCY_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, "replayed:", res.Header.Get("Idempotency-Replayed"))
fmt.Println(string(body))
}
// .NET 8: in a new console project (dotnet new console), replace Program.cs with this.
using System.Net.Http.Json;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, $"{Environment.GetEnvironmentVariable("FIELDTASK_API_ORIGIN")}/public/v1/jobs")
{
Content = JsonContent.Create(new { name = "Quickstart test job", customer_id = "11111111-1111-4111-8111-111111111111" }),
};
request.Headers.Add("Authorization", $"Bearer {Environment.GetEnvironmentVariable("FIELDTASK_API_TOKEN")}");
request.Headers.Add("Idempotency-Key", Environment.GetEnvironmentVariable("IDEMPOTENCY_KEY"));
var response = await client.SendAsync(request);
var replayed = response.Headers.TryGetValues("Idempotency-Replayed", out var values) ? values.First() : "";
Console.WriteLine($"{(int)response.StatusCode} replayed: {replayed}\n{await response.Content.ReadAsStringAsync()}");
// Cargo.toml: reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] }
// serde_json = "1"
use std::env;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let response = reqwest::blocking::Client::new()
.post(format!("{}/public/v1/jobs", env::var("FIELDTASK_API_ORIGIN")?))
.bearer_auth(env::var("FIELDTASK_API_TOKEN")?)
.header("Idempotency-Key", env::var("IDEMPOTENCY_KEY")?)
.json(&serde_json::json!({
"name": "Quickstart test job",
"customer_id": "11111111-1111-4111-8111-111111111111"
}))
.send()?;
let replayed = response.headers().get("Idempotency-Replayed").and_then(|v| v.to_str().ok()).unwrap_or("").to_owned();
println!("{} replayed: {}\n{}", response.status(), replayed, response.text()?);
Ok(())
}
Replace customer_id with the ID from step 2. You get 201 Created, the job in data, and headers including Location (the job's URL) and Operation-Location.
The job now appears under Jobs in FieldTask.
4. Retry safely
Run the same command or program again in the same shell, without generating a new key. You get the same 201 response with Idempotency-Replayed: true, and no second job is created. That's how to retry after a timeout. See Idempotency and recovery.
Next steps
- Schedule, locate and assign jobs: Create a job.
- Read all pages of a list: Pagination.
- Handle errors: Errors.
- Keep your system in sync: Webhooks or polling?