Quickstart
Authorise with HMRC, retrieve tax information, then prepare a submission for the user to approve on GoFile. No practice creation or client registration is needed.
All calls use signed POST requests to https://api.gofile.co.uk. Put connection IDs and tax identifiers in the JSON body, not the URL or headers.
| Action | VAT | Income Tax |
|---|---|---|
| Authorise or check completion | /api/v1/authorise/vat | /api/v1/authorise/itsa |
| Retrieve information or a receipt | /api/v1/vat | /api/v1/itsa |
| Prepare a submission | /api/v1/vat/prepare | /api/v1/itsa/prepare |
1. Sign API requests
Create a sandbox key in API keys. Signed-in integration owners see their sandbox credentials in these examples. Every API example has PHP, Python, Node.js and curl tabs, with Copy and Download. Each is self-contained; PHP needs curl, and Python and Node.js use their standard libraries. Keep credentials on your backend.
<?php
// #GoFile Connect API — plain PHP, no composer, no dependencies beyond curl.
function gofile($path, $body = null, $idempotencyKey = null) {
$base = 'https://api.gofile.co.uk';
$key = 'gfk_sandbox_your_key_id_here';
$secret = 'gfs_sandbox_your_secret_here';
$raw = $body === null ? '' : json_encode($body);
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$canonical = $path . "\n" . $ts . "\n" . $nonce . "\n" . hash('sha256', $raw);
$headers = array(
'Content-Type: application/json',
'X-GoFile-Key: ' . $key,
'X-GoFile-Timestamp: ' . $ts,
'X-GoFile-Nonce: ' . $nonce,
'X-GoFile-Signature: ' . hash_hmac('sha256', $canonical, $secret),
);
if ($idempotencyKey !== null) $headers[] = 'Idempotency-Key: ' . $idempotencyKey;
$ch = curl_init($base . $path);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if ($raw !== '') curl_setopt($ch, CURLOPT_POSTFIELDS, $raw);
$out = curl_exec($ch);
curl_close($ch);
return json_decode($out);
}
# #GoFile Connect API — Python 3 standard library only.
import hashlib, hmac, json, secrets, time, urllib.request
BASE = 'https://api.gofile.co.uk'
KEY = 'gfk_sandbox_your_key_id_here'
SECRET = 'gfs_sandbox_your_secret_here'
def gofile(path, body=None, idempotency_key=None):
raw = b'' if body is None else json.dumps(body).encode()
ts = str(int(time.time()))
nonce = secrets.token_hex(16)
canonical = '\n'.join([path, ts, nonce, hashlib.sha256(raw).hexdigest()])
sig = hmac.new(SECRET.encode(), canonical.encode(), hashlib.sha256).hexdigest()
req = urllib.request.Request(BASE + path, data=raw or None, method='POST', headers={
'Content-Type': 'application/json', 'X-GoFile-Key': KEY,
'X-GoFile-Timestamp': ts, 'X-GoFile-Nonce': nonce, 'X-GoFile-Signature': sig,
})
if idempotency_key is not None:
req.add_header('Idempotency-Key', idempotency_key)
try:
with urllib.request.urlopen(req) as r:
return r.status, json.loads(r.read())
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read())
// #GoFile Connect API — Node.js built-ins only (any Node from v10 up).
var https = require('https');
var crypto = require('crypto');
var BASE = 'api.gofile.co.uk';
var KEY = 'gfk_sandbox_your_key_id_here';
var SECRET = 'gfs_sandbox_your_secret_here';
function gofile(path, body, done, idempotencyKey) {
var raw = body ? JSON.stringify(body) : '';
var ts = String(Math.floor(Date.now() / 1000));
var nonce = crypto.randomBytes(16).toString('hex');
var bodyHash = crypto.createHash('sha256').update(raw).digest('hex');
var canonical = [path, ts, nonce, bodyHash].join('\n');
var sig = crypto.createHmac('sha256', SECRET).update(canonical).digest('hex');
var req = https.request({ host: BASE, path: path, method: 'POST', headers: {
'Content-Type': 'application/json', 'X-GoFile-Key': KEY,
'X-GoFile-Timestamp': ts, 'X-GoFile-Nonce': nonce, 'X-GoFile-Signature': sig,
}}, function (res) {
var data = '';
res.on('data', function (c) { data += c; });
res.on('end', function () { done(res.statusCode, JSON.parse(data)); });
});
if (idempotencyKey) req.setHeader('Idempotency-Key', idempotencyKey);
if (raw) req.write(raw);
req.end();
}
#!/usr/bin/env bash
# #GoFile Connect API — bash + curl + openssl, nothing else.
BASE="https://api.gofile.co.uk"
KEY="gfk_sandbox_your_key_id_here"
SECRET="gfs_sandbox_your_secret_here"
gofile() { # PATH [JSON_BODY] [IDEMPOTENCY_KEY]
local path="$1" body="${2:-}" idem="${3:-}" ts nonce body_hash canonical sig
ts="$(date +%s)"; nonce="$(openssl rand -hex 16)"
body_hash="$(printf '%s' "$body" | openssl dgst -sha256 -r | cut -d' ' -f1)"
canonical="$(printf '%s\n%s\n%s\n%s' "$path" "$ts" "$nonce" "$body_hash")"
sig="$(printf '%s' "$canonical" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)"
curl -sS -X "POST" "$BASE$path" \
-H "Content-Type: application/json" -H "X-GoFile-Key: $KEY" \
-H "X-GoFile-Timestamp: $ts" -H "X-GoFile-Nonce: $nonce" \
-H "X-GoFile-Signature: $sig" -H "Idempotency-Key: $idem" ${body:+--data "$body"}
}
The signature is hex HMAC-SHA256 of path\ntimestamp\nnonce\nsha256(body), with no trailing newline. Sign the exact bytes you send. The headers are X-GoFile-Key, X-GoFile-Timestamp (Unix seconds), X-GoFile-Nonce (fresh for each request), and X-GoFile-Signature, plus Content-Type: application/json. There is no method line: every call is a POST, and any other method is refused before the signature is checked. A request with a query string is refused as well (422 validation_failed), so the signed path is always the bare route. There is no separate credentials-check endpoint.
Every signed API response has request_id, fetched_at, state and data. Read state at the top level; see Errors and troubleshooting when it is error.
Your timestamp must be within five minutes of GoFile's server time. See operational limits and the states and next actions.
2. Authorise VAT
Start the journey and save its temporary reference against your customer. The PHP example saves it in the server session and redirects the browser; the Python, Node.js and curl examples print the session ID and URL to open. Use your authenticated software user's ID in place of user-42 and the customer's VAT number in place of 999999999: a new connection names the client it is for, and GoFile fetches that client's information the moment HMRC consent completes, before the user is returned to your software. No callback URL or end-user GoFile login is needed.
<?php
// #GoFile Connect API — plain PHP, no composer, no dependencies beyond curl.
function gofile($path, $body = null, $idempotencyKey = null) {
$base = 'https://api.gofile.co.uk';
$key = 'gfk_sandbox_your_key_id_here';
$secret = 'gfs_sandbox_your_secret_here';
$raw = $body === null ? '' : json_encode($body);
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$canonical = $path . "\n" . $ts . "\n" . $nonce . "\n" . hash('sha256', $raw);
$headers = array(
'Content-Type: application/json',
'X-GoFile-Key: ' . $key,
'X-GoFile-Timestamp: ' . $ts,
'X-GoFile-Nonce: ' . $nonce,
'X-GoFile-Signature: ' . hash_hmac('sha256', $canonical, $secret),
);
if ($idempotencyKey !== null) $headers[] = 'Idempotency-Key: ' . $idempotencyKey;
$ch = curl_init($base . $path);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if ($raw !== '') curl_setopt($ch, CURLOPT_POSTFIELDS, $raw);
$out = curl_exec($ch);
curl_close($ch);
return json_decode($out);
}
// Demo session storage. In production, save this temporary attempt against your customer.
session_start();
// Replace user-42 with your authenticated user's ID and 999999999 with the customer's nine-digit VAT number.
$preauth = gofile('/api/v1/authorise/vat', array('software_user_id' => 'user-42', 'vat_number' => '999999999'));
$_SESSION['gofile_authorisation']['vat'] = $preauth->data->authorisation_session_id;
header('Location: ' . $preauth->data->authorisation_url);
exit;
# #GoFile Connect API — Python 3 standard library only.
import hashlib, hmac, json, secrets, time, urllib.request
BASE = 'https://api.gofile.co.uk'
KEY = 'gfk_sandbox_your_key_id_here'
SECRET = 'gfs_sandbox_your_secret_here'
def gofile(path, body=None, idempotency_key=None):
raw = b'' if body is None else json.dumps(body).encode()
ts = str(int(time.time()))
nonce = secrets.token_hex(16)
canonical = '\n'.join([path, ts, nonce, hashlib.sha256(raw).hexdigest()])
sig = hmac.new(SECRET.encode(), canonical.encode(), hashlib.sha256).hexdigest()
req = urllib.request.Request(BASE + path, data=raw or None, method='POST', headers={
'Content-Type': 'application/json', 'X-GoFile-Key': KEY,
'X-GoFile-Timestamp': ts, 'X-GoFile-Nonce': nonce, 'X-GoFile-Signature': sig,
})
if idempotency_key is not None:
req.add_header('Idempotency-Key', idempotency_key)
try:
with urllib.request.urlopen(req) as r:
return r.status, json.loads(r.read())
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read())
# Use the current authenticated user ID from your backend.
status, preauth = gofile('/api/v1/authorise/vat', {'software_user_id': 'user-42', 'vat_number': '999999999'})
# Save this temporary attempt against your customer before opening the URL.
session_id = preauth['data']['authorisation_session_id']
print('Session:', session_id)
print('Open in your browser:', preauth['data']['authorisation_url'])
// #GoFile Connect API — Node.js built-ins only (any Node from v10 up).
var https = require('https');
var crypto = require('crypto');
var BASE = 'api.gofile.co.uk';
var KEY = 'gfk_sandbox_your_key_id_here';
var SECRET = 'gfs_sandbox_your_secret_here';
function gofile(path, body, done, idempotencyKey) {
var raw = body ? JSON.stringify(body) : '';
var ts = String(Math.floor(Date.now() / 1000));
var nonce = crypto.randomBytes(16).toString('hex');
var bodyHash = crypto.createHash('sha256').update(raw).digest('hex');
var canonical = [path, ts, nonce, bodyHash].join('\n');
var sig = crypto.createHmac('sha256', SECRET).update(canonical).digest('hex');
var req = https.request({ host: BASE, path: path, method: 'POST', headers: {
'Content-Type': 'application/json', 'X-GoFile-Key': KEY,
'X-GoFile-Timestamp': ts, 'X-GoFile-Nonce': nonce, 'X-GoFile-Signature': sig,
}}, function (res) {
var data = '';
res.on('data', function (c) { data += c; });
res.on('end', function () { done(res.statusCode, JSON.parse(data)); });
});
if (idempotencyKey) req.setHeader('Idempotency-Key', idempotencyKey);
if (raw) req.write(raw);
req.end();
}
// Use the current authenticated user ID from your backend.
gofile('/api/v1/authorise/vat', {software_user_id: 'user-42', vat_number: '999999999'}, function (status, preauth) {
// Save this temporary attempt against your customer before opening the URL.
var sessionId = preauth.data.authorisation_session_id;
console.log('Session:', sessionId);
console.log('Open in your browser:', preauth.data.authorisation_url);
});
#!/usr/bin/env bash
# #GoFile Connect API — bash + curl + openssl, nothing else.
BASE="https://api.gofile.co.uk"
KEY="gfk_sandbox_your_key_id_here"
SECRET="gfs_sandbox_your_secret_here"
gofile() { # PATH [JSON_BODY] [IDEMPOTENCY_KEY]
local path="$1" body="${2:-}" idem="${3:-}" ts nonce body_hash canonical sig
ts="$(date +%s)"; nonce="$(openssl rand -hex 16)"
body_hash="$(printf '%s' "$body" | openssl dgst -sha256 -r | cut -d' ' -f1)"
canonical="$(printf '%s\n%s\n%s\n%s' "$path" "$ts" "$nonce" "$body_hash")"
sig="$(printf '%s' "$canonical" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)"
curl -sS -X "POST" "$BASE$path" \
-H "Content-Type: application/json" -H "X-GoFile-Key: $KEY" \
-H "X-GoFile-Timestamp: $ts" -H "X-GoFile-Nonce: $nonce" \
-H "X-GoFile-Signature: $sig" -H "Idempotency-Key: $idem" ${body:+--data "$body"}
}
# Requires jq. Use the current authenticated user ID from your backend.
REPLY="$(gofile /api/v1/authorise/vat '{"software_user_id":"user-42","vat_number":"999999999"}')"
# Save this temporary attempt against your customer before opening the URL.
SESSION="$(printf '%s' "$REPLY" | jq -r '.data.authorisation_session_id')"
printf 'Session: %s\nOpen in your browser: ' "$SESSION"
printf '%s' "$REPLY" | jq -r '.data.authorisation_url'
After authorising, run the result example. PHP uses the same browser session; for Python, Node.js and curl, set GOFILE_AUTHORISATION_SESSION to the printed session ID. In your product, load the saved attempt on your backend automatically. The example captures the connection ID only when complete.
<?php
// #GoFile Connect API — plain PHP, no composer, no dependencies beyond curl.
function gofile($path, $body = null, $idempotencyKey = null) {
$base = 'https://api.gofile.co.uk';
$key = 'gfk_sandbox_your_key_id_here';
$secret = 'gfs_sandbox_your_secret_here';
$raw = $body === null ? '' : json_encode($body);
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$canonical = $path . "\n" . $ts . "\n" . $nonce . "\n" . hash('sha256', $raw);
$headers = array(
'Content-Type: application/json',
'X-GoFile-Key: ' . $key,
'X-GoFile-Timestamp: ' . $ts,
'X-GoFile-Nonce: ' . $nonce,
'X-GoFile-Signature: ' . hash_hmac('sha256', $canonical, $secret),
);
if ($idempotencyKey !== null) $headers[] = 'Idempotency-Key: ' . $idempotencyKey;
$ch = curl_init($base . $path);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if ($raw !== '') curl_setopt($ch, CURLOPT_POSTFIELDS, $raw);
$out = curl_exec($ch);
curl_close($ch);
return json_decode($out);
}
// Demo session storage. In production, load the customer's saved attempt from your database.
session_start();
$sessionId = $_SESSION['gofile_authorisation']['vat'];
$auth = gofile('/api/v1/authorise/vat', array('authorisation_session_id' => $sessionId));
// Save the connection ID only after GoFile confirms authorisation.
if ($auth->state === 'completed') {
// Production: save connection_id encrypted in your customer database.
$_SESSION['gofile_connections']['vat'] = $auth->data->connection_id;
}
# #GoFile Connect API — Python 3 standard library only.
import hashlib, hmac, json, secrets, time, urllib.request
BASE = 'https://api.gofile.co.uk'
KEY = 'gfk_sandbox_your_key_id_here'
SECRET = 'gfs_sandbox_your_secret_here'
def gofile(path, body=None, idempotency_key=None):
raw = b'' if body is None else json.dumps(body).encode()
ts = str(int(time.time()))
nonce = secrets.token_hex(16)
canonical = '\n'.join([path, ts, nonce, hashlib.sha256(raw).hexdigest()])
sig = hmac.new(SECRET.encode(), canonical.encode(), hashlib.sha256).hexdigest()
req = urllib.request.Request(BASE + path, data=raw or None, method='POST', headers={
'Content-Type': 'application/json', 'X-GoFile-Key': KEY,
'X-GoFile-Timestamp': ts, 'X-GoFile-Nonce': nonce, 'X-GoFile-Signature': sig,
})
if idempotency_key is not None:
req.add_header('Idempotency-Key', idempotency_key)
try:
with urllib.request.urlopen(req) as r:
return r.status, json.loads(r.read())
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read())
import os
# Demo: set GOFILE_AUTHORISATION_SESSION to the session ID printed at start.
# Production: load this customer's saved attempt from your database.
session_id = os.environ['GOFILE_AUTHORISATION_SESSION']
status, auth = gofile('/api/v1/authorise/vat', {'authorisation_session_id': session_id})
# Save the connection only after GoFile confirms authorisation.
if auth['state'] == 'completed':
connection_id = auth['data']['connection_id']
# Production: save connection_id encrypted in your customer database.
print('Save connection ID:', connection_id)
else:
print('Authorisation:', auth['state'])
// #GoFile Connect API — Node.js built-ins only (any Node from v10 up).
var https = require('https');
var crypto = require('crypto');
var BASE = 'api.gofile.co.uk';
var KEY = 'gfk_sandbox_your_key_id_here';
var SECRET = 'gfs_sandbox_your_secret_here';
function gofile(path, body, done, idempotencyKey) {
var raw = body ? JSON.stringify(body) : '';
var ts = String(Math.floor(Date.now() / 1000));
var nonce = crypto.randomBytes(16).toString('hex');
var bodyHash = crypto.createHash('sha256').update(raw).digest('hex');
var canonical = [path, ts, nonce, bodyHash].join('\n');
var sig = crypto.createHmac('sha256', SECRET).update(canonical).digest('hex');
var req = https.request({ host: BASE, path: path, method: 'POST', headers: {
'Content-Type': 'application/json', 'X-GoFile-Key': KEY,
'X-GoFile-Timestamp': ts, 'X-GoFile-Nonce': nonce, 'X-GoFile-Signature': sig,
}}, function (res) {
var data = '';
res.on('data', function (c) { data += c; });
res.on('end', function () { done(res.statusCode, JSON.parse(data)); });
});
if (idempotencyKey) req.setHeader('Idempotency-Key', idempotencyKey);
if (raw) req.write(raw);
req.end();
}
// Demo: set GOFILE_AUTHORISATION_SESSION to the session ID printed at start.
// Production: load this customer's saved attempt from your database.
var sessionId = process.env.GOFILE_AUTHORISATION_SESSION;
gofile('/api/v1/authorise/vat', {authorisation_session_id: sessionId}, function (status, auth) {
// Save the connection only after GoFile confirms authorisation.
if (auth.state === 'completed') {
var connectionId = auth.data.connection_id;
// Production: save connectionId encrypted in your customer database.
console.log('Save connection ID:', connectionId);
} else {
console.log('Authorisation:', auth.state);
}
});
#!/usr/bin/env bash
# #GoFile Connect API — bash + curl + openssl, nothing else.
BASE="https://api.gofile.co.uk"
KEY="gfk_sandbox_your_key_id_here"
SECRET="gfs_sandbox_your_secret_here"
gofile() { # PATH [JSON_BODY] [IDEMPOTENCY_KEY]
local path="$1" body="${2:-}" idem="${3:-}" ts nonce body_hash canonical sig
ts="$(date +%s)"; nonce="$(openssl rand -hex 16)"
body_hash="$(printf '%s' "$body" | openssl dgst -sha256 -r | cut -d' ' -f1)"
canonical="$(printf '%s\n%s\n%s\n%s' "$path" "$ts" "$nonce" "$body_hash")"
sig="$(printf '%s' "$canonical" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)"
curl -sS -X "POST" "$BASE$path" \
-H "Content-Type: application/json" -H "X-GoFile-Key: $KEY" \
-H "X-GoFile-Timestamp: $ts" -H "X-GoFile-Nonce: $nonce" \
-H "X-GoFile-Signature: $sig" -H "Idempotency-Key: $idem" ${body:+--data "$body"}
}
# Requires jq. Set GOFILE_AUTHORISATION_SESSION to the session ID printed at start.
# Production: load this customer's saved attempt from your database.
SESSION="${GOFILE_AUTHORISATION_SESSION:?Set the saved authorisation session ID}"
BODY="$(jq -cn --arg id "$SESSION" '{authorisation_session_id:$id}')"
REPLY="$(gofile /api/v1/authorise/vat "$BODY")"
# Save the connection only after GoFile confirms authorisation.
if [ "$(printf '%s' "$REPLY" | jq -r '.state')" = completed ]; then
CONNECTION="$(printf '%s' "$REPLY" | jq -r '.data.connection_id')"
# Production: save CONNECTION encrypted in your customer database.
printf 'Save connection ID: %s\n' "$CONNECTION"
else
printf 'Authorisation: '
printf '%s' "$REPLY" | jq -r '.state'
fi
For automatic completion, the complete browser example opens a separate window and checks the result through your backend. A redirect ends the original PHP request; it does not resume it after HMRC consent.
The examples use PHP sessions for testing. In your product, save completed connection IDs encrypted in your database against the customer, service and environment. See Authorisation basics for the storage map, renewal and optional JavaScript buttons.
3. Retrieve VAT information
Use the connection saved in step 2. PHP reads its demo session; insert the returned connection ID in the other language examples. 999999999 is a dummy VAT number for the built-in simulator, which sends nothing to HMRC. Replace it with the authorised customer's VAT number for live calls.
<?php
// #GoFile Connect API — plain PHP, no composer, no dependencies beyond curl.
function gofile($path, $body = null, $idempotencyKey = null) {
$base = 'https://api.gofile.co.uk';
$key = 'gfk_sandbox_your_key_id_here';
$secret = 'gfs_sandbox_your_secret_here';
$raw = $body === null ? '' : json_encode($body);
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$canonical = $path . "\n" . $ts . "\n" . $nonce . "\n" . hash('sha256', $raw);
$headers = array(
'Content-Type: application/json',
'X-GoFile-Key: ' . $key,
'X-GoFile-Timestamp: ' . $ts,
'X-GoFile-Nonce: ' . $nonce,
'X-GoFile-Signature: ' . hash_hmac('sha256', $canonical, $secret),
);
if ($idempotencyKey !== null) $headers[] = 'Idempotency-Key: ' . $idempotencyKey;
$ch = curl_init($base . $path);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if ($raw !== '') curl_setopt($ch, CURLOPT_POSTFIELDS, $raw);
$out = curl_exec($ch);
curl_close($ch);
return json_decode($out);
}
// Demo session storage. In production, load this customer's connection from your encrypted database.
session_start();
$connectionId = $_SESSION['gofile_connections']['vat'];
$vatNumber = '999999999'; // Sandbox placeholder; replace for live calls.
// Retrieve authorisation standing, obligations and balance.
$status = gofile('/api/v1/vat', array('connection_id' => $connectionId, 'vat_number' => $vatNumber));
# #GoFile Connect API — Python 3 standard library only.
import hashlib, hmac, json, secrets, time, urllib.request
BASE = 'https://api.gofile.co.uk'
KEY = 'gfk_sandbox_your_key_id_here'
SECRET = 'gfs_sandbox_your_secret_here'
def gofile(path, body=None, idempotency_key=None):
raw = b'' if body is None else json.dumps(body).encode()
ts = str(int(time.time()))
nonce = secrets.token_hex(16)
canonical = '\n'.join([path, ts, nonce, hashlib.sha256(raw).hexdigest()])
sig = hmac.new(SECRET.encode(), canonical.encode(), hashlib.sha256).hexdigest()
req = urllib.request.Request(BASE + path, data=raw or None, method='POST', headers={
'Content-Type': 'application/json', 'X-GoFile-Key': KEY,
'X-GoFile-Timestamp': ts, 'X-GoFile-Nonce': nonce, 'X-GoFile-Signature': sig,
})
if idempotency_key is not None:
req.add_header('Idempotency-Key', idempotency_key)
try:
with urllib.request.urlopen(req) as r:
return r.status, json.loads(r.read())
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read())
ACCOUNT = 'hmc_your_connection_id_here'
CLIENT = '999999999'
status, reply = gofile('/api/v1/vat', {'connection_id': ACCOUNT, 'vat_number': CLIENT})
print('HTTP', status, '-', reply['state'])
if reply['state'] == 'action_required':
print('Send your user to:', reply['data']['authorisation']['continue_url'])
else:
print(json.dumps(reply['data'], indent=2))
// #GoFile Connect API — Node.js built-ins only (any Node from v10 up).
var https = require('https');
var crypto = require('crypto');
var BASE = 'api.gofile.co.uk';
var KEY = 'gfk_sandbox_your_key_id_here';
var SECRET = 'gfs_sandbox_your_secret_here';
function gofile(path, body, done, idempotencyKey) {
var raw = body ? JSON.stringify(body) : '';
var ts = String(Math.floor(Date.now() / 1000));
var nonce = crypto.randomBytes(16).toString('hex');
var bodyHash = crypto.createHash('sha256').update(raw).digest('hex');
var canonical = [path, ts, nonce, bodyHash].join('\n');
var sig = crypto.createHmac('sha256', SECRET).update(canonical).digest('hex');
var req = https.request({ host: BASE, path: path, method: 'POST', headers: {
'Content-Type': 'application/json', 'X-GoFile-Key': KEY,
'X-GoFile-Timestamp': ts, 'X-GoFile-Nonce': nonce, 'X-GoFile-Signature': sig,
}}, function (res) {
var data = '';
res.on('data', function (c) { data += c; });
res.on('end', function () { done(res.statusCode, JSON.parse(data)); });
});
if (idempotencyKey) req.setHeader('Idempotency-Key', idempotencyKey);
if (raw) req.write(raw);
req.end();
}
var ACCOUNT = 'hmc_your_connection_id_here';
var CLIENT = '999999999';
gofile('/api/v1/vat', {connection_id: ACCOUNT, vat_number: CLIENT}, function (status, reply) {
console.log('HTTP', status, '-', reply.state);
if (reply.state === 'action_required') {
console.log('Send your user to:', reply.data.authorisation.continue_url);
} else {
console.log(JSON.stringify(reply.data, null, 2));
}
});
#!/usr/bin/env bash
# #GoFile Connect API — bash + curl + openssl, nothing else.
BASE="https://api.gofile.co.uk"
KEY="gfk_sandbox_your_key_id_here"
SECRET="gfs_sandbox_your_secret_here"
gofile() { # PATH [JSON_BODY] [IDEMPOTENCY_KEY]
local path="$1" body="${2:-}" idem="${3:-}" ts nonce body_hash canonical sig
ts="$(date +%s)"; nonce="$(openssl rand -hex 16)"
body_hash="$(printf '%s' "$body" | openssl dgst -sha256 -r | cut -d' ' -f1)"
canonical="$(printf '%s\n%s\n%s\n%s' "$path" "$ts" "$nonce" "$body_hash")"
sig="$(printf '%s' "$canonical" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)"
curl -sS -X "POST" "$BASE$path" \
-H "Content-Type: application/json" -H "X-GoFile-Key: $KEY" \
-H "X-GoFile-Timestamp: $ts" -H "X-GoFile-Nonce: $nonce" \
-H "X-GoFile-Signature: $sig" -H "Idempotency-Key: $idem" ${body:+--data "$body"}
}
ACCOUNT="hmc_your_connection_id_here"
CLIENT="999999999"
gofile /api/v1/vat '{"connection_id":"hmc_your_connection_id_here","vat_number":"999999999"}'
Read the decoded response returned by the helper. state: ready supplies obligations and balance in data; after the authorisation journey the first read is normally ready. syncing means the client's first fetch is still running: wait the returned retry interval (normally seconds). For action_required, follow data.authorisation.continue_url when supplied.
Reads serve GoFile's ledger immediately. A read that finds it older than five minutes queues a background refresh; otherwise it refreshes weekly and 30 minutes after a filing made through GoFile. Check data.data_as_of for actual age. See ledger freshness.
4. Prepare and approve a return
This example prepares a sandbox draft. PHP saves its filing ID in the demo session and redirects to approval; the other examples return or print the ID and approval URL for your software to use. Replace the sample figures and software-user details with your own. Your backend supplies the current user's ID and login from its authenticated session.
<?php
// #GoFile Connect API — plain PHP, no composer, no dependencies beyond curl.
function gofile($path, $body = null, $idempotencyKey = null) {
$base = 'https://api.gofile.co.uk';
$key = 'gfk_sandbox_your_key_id_here';
$secret = 'gfs_sandbox_your_secret_here';
$raw = $body === null ? '' : json_encode($body);
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$canonical = $path . "\n" . $ts . "\n" . $nonce . "\n" . hash('sha256', $raw);
$headers = array(
'Content-Type: application/json',
'X-GoFile-Key: ' . $key,
'X-GoFile-Timestamp: ' . $ts,
'X-GoFile-Nonce: ' . $nonce,
'X-GoFile-Signature: ' . hash_hmac('sha256', $canonical, $secret),
);
if ($idempotencyKey !== null) $headers[] = 'Idempotency-Key: ' . $idempotencyKey;
$ch = curl_init($base . $path);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if ($raw !== '') curl_setopt($ch, CURLOPT_POSTFIELDS, $raw);
$out = curl_exec($ch);
curl_close($ch);
return json_decode($out);
}
// Demo session storage. In production, load this customer's connection from your encrypted database.
session_start();
$connectionId = $_SESSION['gofile_connections']['vat'];
$vatNumber = '999999999'; // Sandbox placeholder; replace for live calls.
// Example sandbox figures. Replace with your own.
$draft = array(
'connection_id' => $connectionId,
'vat_number' => $vatNumber,
'software_user_id' => 'user-42', // Your current authenticated software user.
'software_user_login' => '[email protected]', // Their login identifier.
// period omitted: the sole open obligation period is resolved for you
'return' => array(
'vat_due_sales_pence' => 1250000, 'vat_due_acquisitions_pence' => 0,
'total_vat_due_pence' => 1250000, 'vat_reclaimed_current_period_pence' => 420000,
'net_vat_due_pence' => 830000, 'total_value_sales_ex_vat_pence' => 6250000,
'total_value_purchases_ex_vat_pence' => 2100000,
'total_value_goods_supplied_ex_vat_pence' => 0, 'total_acquisitions_ex_vat_pence' => 0,
),
);
// Prepare the return and save its ID so you can retrieve the receipt.
// Save one retry key per return in your database; reuse it for the same draft.
$retryKey = $_SESSION['gofile_retry_keys']['vat-demo'] ??= bin2hex(random_bytes(16));
$filing = gofile("/api/v1/vat/prepare", $draft, $retryKey);
// Production: save data.submission_id in your database against this return.
$_SESSION['gofile_filings']['vat'] = $filing->data->submission_id;
// Send the user to GoFile to review and approve the return.
header('Location: ' . $filing->data->continue_url);
exit;
# #GoFile Connect API — Python 3 standard library only.
import hashlib, hmac, json, secrets, time, urllib.request
BASE = 'https://api.gofile.co.uk'
KEY = 'gfk_sandbox_your_key_id_here'
SECRET = 'gfs_sandbox_your_secret_here'
def gofile(path, body=None, idempotency_key=None):
raw = b'' if body is None else json.dumps(body).encode()
ts = str(int(time.time()))
nonce = secrets.token_hex(16)
canonical = '\n'.join([path, ts, nonce, hashlib.sha256(raw).hexdigest()])
sig = hmac.new(SECRET.encode(), canonical.encode(), hashlib.sha256).hexdigest()
req = urllib.request.Request(BASE + path, data=raw or None, method='POST', headers={
'Content-Type': 'application/json', 'X-GoFile-Key': KEY,
'X-GoFile-Timestamp': ts, 'X-GoFile-Nonce': nonce, 'X-GoFile-Signature': sig,
})
if idempotency_key is not None:
req.add_header('Idempotency-Key', idempotency_key)
try:
with urllib.request.urlopen(req) as r:
return r.status, json.loads(r.read())
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read())
ACCOUNT = 'hmc_your_connection_id_here'
CLIENT = '999999999'
draft = {
'connection_id': ACCOUNT,
'vat_number': CLIENT,
'software_user_id': 'user-42',
'software_user_login': '[email protected]',
# period omitted: the sole open obligation period is resolved for you
'return': {
'vat_due_sales_pence': 1250000, 'vat_due_acquisitions_pence': 0,
'total_vat_due_pence': 1250000, 'vat_reclaimed_current_period_pence': 420000,
'net_vat_due_pence': 830000, 'total_value_sales_ex_vat_pence': 6250000,
'total_value_purchases_ex_vat_pence': 2100000,
'total_value_goods_supplied_ex_vat_pence': 0, 'total_acquisitions_ex_vat_pence': 0,
},
}
status, reply = gofile('/api/v1/vat/prepare', draft, 'vat-return-2026-q2') # Save and reuse this key for this draft.
if status in (200, 201):
print('Filing', reply['data']['submission_id'])
print('Send your user to approve:', reply['data']['continue_url'])
else:
print('HTTP', status)
print(json.dumps(reply, indent=2))
// #GoFile Connect API — Node.js built-ins only (any Node from v10 up).
var https = require('https');
var crypto = require('crypto');
var BASE = 'api.gofile.co.uk';
var KEY = 'gfk_sandbox_your_key_id_here';
var SECRET = 'gfs_sandbox_your_secret_here';
function gofile(path, body, done, idempotencyKey) {
var raw = body ? JSON.stringify(body) : '';
var ts = String(Math.floor(Date.now() / 1000));
var nonce = crypto.randomBytes(16).toString('hex');
var bodyHash = crypto.createHash('sha256').update(raw).digest('hex');
var canonical = [path, ts, nonce, bodyHash].join('\n');
var sig = crypto.createHmac('sha256', SECRET).update(canonical).digest('hex');
var req = https.request({ host: BASE, path: path, method: 'POST', headers: {
'Content-Type': 'application/json', 'X-GoFile-Key': KEY,
'X-GoFile-Timestamp': ts, 'X-GoFile-Nonce': nonce, 'X-GoFile-Signature': sig,
}}, function (res) {
var data = '';
res.on('data', function (c) { data += c; });
res.on('end', function () { done(res.statusCode, JSON.parse(data)); });
});
if (idempotencyKey) req.setHeader('Idempotency-Key', idempotencyKey);
if (raw) req.write(raw);
req.end();
}
// Production: load this customer's connection and VAT number from your database.
var connectionId = 'hmc_your_connection_id_here';
var vatNumber = '999999999'; // Dummy simulator VAT number; replace for live calls.
var draft = {
connection_id: connectionId,
vat_number: vatNumber,
software_user_id: 'user-42', // Current authenticated software user.
software_user_login: '[email protected]', // Their login identifier.
// Period omitted: GoFile resolves the sole open obligation.
return: {
vat_due_sales_pence: 1250000, vat_due_acquisitions_pence: 0,
total_vat_due_pence: 1250000, vat_reclaimed_current_period_pence: 420000,
net_vat_due_pence: 830000, total_value_sales_ex_vat_pence: 6250000,
total_value_purchases_ex_vat_pence: 2100000,
total_value_goods_supplied_ex_vat_pence: 0, total_acquisitions_ex_vat_pence: 0
}
};
// Prepare, then save the filing ID before opening hosted approval.
gofile('/api/v1/vat/prepare', draft, function (status, reply) {
if (status === 200 || status === 201) {
console.log('Filing:', reply.data.submission_id); // Save against your return in the database.
console.log('Send your user to approve:', reply.data.continue_url);
} else {
console.log('HTTP', status, reply);
}
}, 'vat-return-2026-q2'); // Save and reuse this key for this draft.
#!/usr/bin/env bash
# #GoFile Connect API — bash + curl + openssl, nothing else.
BASE="https://api.gofile.co.uk"
KEY="gfk_sandbox_your_key_id_here"
SECRET="gfs_sandbox_your_secret_here"
gofile() { # PATH [JSON_BODY] [IDEMPOTENCY_KEY]
local path="$1" body="${2:-}" idem="${3:-}" ts nonce body_hash canonical sig
ts="$(date +%s)"; nonce="$(openssl rand -hex 16)"
body_hash="$(printf '%s' "$body" | openssl dgst -sha256 -r | cut -d' ' -f1)"
canonical="$(printf '%s\n%s\n%s\n%s' "$path" "$ts" "$nonce" "$body_hash")"
sig="$(printf '%s' "$canonical" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)"
curl -sS -X "POST" "$BASE$path" \
-H "Content-Type: application/json" -H "X-GoFile-Key: $KEY" \
-H "X-GoFile-Timestamp: $ts" -H "X-GoFile-Nonce: $nonce" \
-H "X-GoFile-Signature: $sig" -H "Idempotency-Key: $idem" ${body:+--data "$body"}
}
ACCOUNT="hmc_your_connection_id_here"
CLIENT="999999999"
BODY='{"connection_id":"hmc_your_connection_id_here","vat_number":"999999999","software_user_id":"user-42","software_user_login":"[email protected]","return":{"vat_due_sales_pence":1250000,"vat_due_acquisitions_pence":0,"total_vat_due_pence":1250000,"vat_reclaimed_current_period_pence":420000,"net_vat_due_pence":830000,"total_value_sales_ex_vat_pence":6250000,"total_value_purchases_ex_vat_pence":2100000,"total_value_goods_supplied_ex_vat_pence":0,"total_acquisitions_ex_vat_pence":0}}'
gofile "/api/v1/vat/prepare" "$BODY" "vat-return-2026-q2"
Both preparation endpoints require an Idempotency-Key. Save one key per draft before the first request and reuse it for an unchanged retry; use a new key for a new draft.
Preparing does not submit. The user reviews the figures and declaration, then selects Confirm & Submit. GoFile displays the result. Save the filing ID against your return in the database before redirecting; use it to retrieve the receipt later.
The VAT guide explains the fields and receipts. The Income Tax guide covers the equivalent quarterly-update journey. Before live filing, supply software-user MFA evidence, persist the draft and retry key, and enable billing in the developer portal.
Downloads and contracts
Command-line diagnostics: PHP, TypeScript, Python, Bash. They read GOFILE_API_BASE, GOFILE_KEY_ID and GOFILE_SECRET. Run once, complete the displayed link, then rerun. Change GOFILE_JOURNEY_KEY (default quickstart-vat) for a new journey.
OpenAPI and JSON Schemas contain complete field definitions. AI context links the same five guides; MCP and tools are optional. Each guide also has a Markdown version.
The current contract is /api/v1, returned in X-GoFile-API-Version. Sandbox and live use the same routes; the signing key selects the environment. Manage keys, permissions, billing and webhook settings in the developer portal.
