Tallyfy’s API provides endpoints to upload files up to 100MB and attach them to tasks or kick-off forms as well as download retrieve metadata for and permanently delete those file assets.
Get file metadata
GET /organizations/{org_id}/assets/{asset_id}
This endpoint retrieves metadata for a specific uploaded file (called an “asset” in Tallyfy’s API). It returns details like the filename, upload date, and which object the file is attached to.
Replace {org_id} with your Organization ID and {asset_id} with the asset ID of the file you want metadata for.
Authorization: Bearer {your_access_token}Accept: application/jsonX-Tallyfy-Client: APIClient
const accessToken = 'YOUR_PERSONAL_ACCESS_TOKEN';const orgId = 'YOUR_ORGANIZATION_ID';const assetId = 'ASSET_ID_TO_GET_METADATA';const apiUrl = `https://go.tallyfy.com/api/organizations/${orgId}/assets/${assetId}`;
const headers = new Headers();headers.append('Authorization', `Bearer ${accessToken}`);headers.append('Accept', 'application/json');headers.append('X-Tallyfy-Client', 'APIClient');
fetch(apiUrl, { method: 'GET', headers: headers}).then(response => { if (!response.ok) { return response.json() .catch(() => response.text()) .then(errData => { console.error(`Error response for asset ${assetId}:`, errData); throw new Error(`HTTP error! status: ${response.status}`); }); } return response.json();}).then(data => { console.log(`Metadata for asset ${assetId}:`); console.log(JSON.stringify(data, null, 2));}).catch(error => { console.error(`Error getting asset metadata ${assetId}:`, error.message);});import requestsimport jsonimport os
access_token = os.environ.get('TALLYFY_ACCESS_TOKEN', 'YOUR_PERSONAL_ACCESS_TOKEN')org_id = os.environ.get('TALLYFY_ORG_ID', 'YOUR_ORGANIZATION_ID')asset_id = 'ASSET_ID_TO_GET_METADATA'api_url = f'https://go.tallyfy.com/api/organizations/{org_id}/assets/{asset_id}'
headers = { 'Authorization': f'Bearer {access_token}', 'Accept': 'application/json', 'X-Tallyfy-Client': 'APIClient'}
response = Nonetry: response = requests.get(api_url, headers=headers) response.raise_for_status()
metadata = response.json() print(f'Metadata for asset {asset_id}:') print(json.dumps(metadata, indent=4))
except requests.exceptions.HTTPError as http_err: print(f"HTTP error for asset {asset_id}: {http_err}") if response is not None: print(f"Response Body: {response.text}")except requests.exceptions.RequestException as req_err: print(f"Request failed for asset {asset_id}: {req_err}")except json.JSONDecodeError: print(f"Failed to decode JSON for asset {asset_id}") if response is not None: print(f"Response Text: {response.text}")import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.io.IOException;
public class GetFileMetadata { public static void main(String[] args) { String accessToken = System.getenv().getOrDefault("TALLYFY_ACCESS_TOKEN", "YOUR_PERSONAL_ACCESS_TOKEN"); String orgId = System.getenv().getOrDefault("TALLYFY_ORG_ID", "YOUR_ORGANIZATION_ID"); String assetId = "ASSET_ID_TO_GET_METADATA"; String apiUrl = String.format("https://go.tallyfy.com/api/organizations/%s/assets/%s", orgId, assetId);
HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(apiUrl)) .header("Authorization", "Bearer " + accessToken) .header("Accept", "application/json") .header("X-Tallyfy-Client", "APIClient") .GET() .build();
try { HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) { System.out.println("Metadata for asset " + assetId + ":"); System.out.println(response.body()); } else { System.err.println("Failed to get metadata for asset " + assetId + ". Status: " + response.statusCode()); System.err.println("Response Body: " + response.body()); } } catch (IOException | InterruptedException e) { System.err.println("Request failed: " + e.getMessage()); Thread.currentThread().interrupt(); } }}package main
import ( "encoding/json" "fmt" "io/ioutil" "net/http" "os" "time")
func main() { accessToken := os.Getenv("TALLYFY_ACCESS_TOKEN") if accessToken == "" { accessToken = "YOUR_PERSONAL_ACCESS_TOKEN" } orgId := os.Getenv("TALLYFY_ORG_ID") if orgId == "" { orgId = "YOUR_ORGANIZATION_ID" } assetId := "ASSET_ID_TO_GET_METADATA" apiUrl := fmt.Sprintf("https://go.tallyfy.com/api/organizations/%s/assets/%s", orgId, assetId)
client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest("GET", apiUrl, nil) if err != nil { fmt.Printf("Error creating request: %v\n", err) return }
req.Header.Set("Authorization", "Bearer "+accessToken) req.Header.Set("Accept", "application/json") req.Header.Set("X-Tallyfy-Client", "APIClient")
resp, err := client.Do(req) if err != nil { fmt.Printf("Error making request: %v\n", err) return } defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("Error reading response: %v\n", err) return }
if resp.StatusCode != http.StatusOK { fmt.Printf("Failed. Status: %d\nBody: %s\n", resp.StatusCode, string(body)) return }
var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { fmt.Printf("Error parsing JSON: %v\nRaw: %s\n", err, string(body)) return }
prettyJSON, _ := json.MarshalIndent(result, "", " ") fmt.Println(string(prettyJSON))}#include <iostream>#include <string>#include <cpprest/http_client.h>#include <cpprest/filestream.h>
using namespace web;using namespace web::http;using namespace web::http::client;
pplx::task<void> GetAssetMetadata(const utility::string_t& assetId){ utility::string_t accessToken = U("YOUR_PERSONAL_ACCESS_TOKEN"); utility::string_t orgId = U("YOUR_ORGANIZATION_ID"); utility::string_t apiUrl = U("https://go.tallyfy.com/api/organizations/") + orgId + U("/assets/") + assetId;
http_client client(apiUrl);
http_request request(methods::GET); request.headers().add(U("Authorization"), U("Bearer ") + accessToken); request.headers().add(U("Accept"), U("application/json")); request.headers().add(U("X-Tallyfy-Client"), U("APIClient"));
return client.request(request).then([assetId](http_response response) { utility::string_t assetIdW = assetId; if (response.status_code() == status_codes::OK) { return response.extract_json().then([assetIdW](json::value v) { std::wcout << L"Metadata for asset " << assetIdW << L":\n" << v.serialize() << std::endl; }); } else { return response.extract_string().then([response, assetIdW](utility::string_t body) { std::wcerr << L"Failed. Status: " << response.status_code() << std::endl; std::wcerr << L"Body: " << body << std::endl; return pplx::task_from_exception<void>(std::runtime_error("API request failed")); }); } });}
int main(){ utility::string_t assetToGet = U("ASSET_ID_TO_GET_METADATA"); try { GetAssetMetadata(assetToGet).wait(); } catch (const std::exception &e) { std::cerr << "Error: " << e.what() << std::endl; } return 0;}// Requires C++ REST SDK (Casablanca).using System;using System.Net.Http;using System.Net.Http.Headers;using System.Text.Json;using System.Threading.Tasks;
public class TallyfyAssetMetadata{ private static readonly HttpClient client = new HttpClient();
public static async Task GetMetadataAsync(string assetId) { var accessToken = Environment.GetEnvironmentVariable("TALLYFY_ACCESS_TOKEN") ?? "YOUR_PERSONAL_ACCESS_TOKEN"; var orgId = Environment.GetEnvironmentVariable("TALLYFY_ORG_ID") ?? "YOUR_ORGANIZATION_ID"; var apiUrl = $"https://go.tallyfy.com/api/organizations/{orgId}/assets/{assetId}";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Add("X-Tallyfy-Client", "APIClient");
try { HttpResponseMessage response = await client.GetAsync(apiUrl); string responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode) { Console.WriteLine($"Metadata for asset {assetId}:"); var doc = JsonDocument.Parse(responseBody); Console.WriteLine(JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true })); } else { Console.WriteLine($"Failed. Status: {response.StatusCode}"); Console.WriteLine($"Body: {responseBody}"); } } catch (HttpRequestException e) { Console.WriteLine($"Request error: {e.Message}"); } }
// static async Task Main(string[] args) // { // await GetMetadataAsync("ASSET_ID_TO_GET_METADATA"); // }}A successful request returns a 200 OK status and a JSON object with the asset metadata inside a data property.
{ "data": { "id": "ASSET_ID_TO_GET_METADATA", "filename": "report_q1.pdf", "version": 1, "uploaded_from": "capture_id_abc123", "uploaded_at": "2024-03-15T10:30:00Z", "step_id": "step_id_xyz789", "source": "local", "system": null, "uploaded_to_s3": true, "subject": { "id": "run_id_or_checklist_id", "type": "Run" } }}Key fields in the response:
| Field | Description |
|---|---|
id | Unique asset identifier |
filename | Original filename of the uploaded file |
version | Version number of the asset |
uploaded_from | Form field ID the file was uploaded from, or ko_field for kick-off fields |
uploaded_at | Timestamp when the file was uploaded |
step_id | Step ID if the file is attached to a task step (can be null) |
source | Upload source - defaults to local |
system | External storage system if applicable (e.g., Dropbox, Google Drive) |
uploaded_to_s3 | Whether the file was stored in S3 |
subject | Object containing the parent’s id and type (e.g., Run, Checklist, Organization) |
If the asset ID isn’t found or you don’t have permission, you’ll get a 404 or 403 error.
Tallyfy’s API provides two GET endpoints for retrieving uploaded files: one that returns the file inline with its original MIME type for browser display and another with a
/dl suffix that forces a download as an attachment with the original filename preserved in the Content-Disposition header. Tallyfy’s API requires a two-step process to attach files to form fields: first upload the file via a multipart POST to the
/file endpoint to receive an asset object and then use a PUT request to link that asset object to the specific task or kick-off form field using the correct capture IDs and subject parameters. Tallyfy’s API lets you permanently delete an uploaded file and its storage record by sending a DELETE request to the /file/ endpoint with your organization and asset IDs — though organization logos are protected from deletion and an alternative /assets/ endpoint only archives without removing the actual stored file.
Was this helpful?
About Tallyfy
- 2025 Tallyfy, Inc.
- Privacy Policy
- Terms of Use
- Report Issue
- Trademarks