Skip to main content

Requests and Responses

Endpoint​

GET ~/loc-2.v1?<lson>
HEAD ~/loc-2.v1?<lson>
POST ~/loc-2.v1

~ is your Agility instance root, for example https://www.example.com/VersionOne. Responses are application/json.

~/loc-2.img is registered to the same handler and behaves identically; it exists only so that callers can vary the URL for cache-busting purposes.

The Query String Is Not a Normal Query String​

The entire raw query string — everything after the ?, with no name=value structure — is URL-decoded and then parsed as LSON, Agility's lenient JSON dialect. What you send determines the shape of what you get.

Array in, Object Out​

This is the form you almost always want.

Bulk request
curl "https://www.example.com/VersionOne/loc-2.v1?[Scope,Timebox,Story]" \
-H "Accept-Language: en-US"
{
"Scope": "Project",
"Timebox": "Sprint",
"Story": "Story"
}

Keys are the tags exactly as you sent them, in request order. Duplicate tags collapse into a single entry.

Scalar in, String Out​

Single-tag request
curl "https://www.example.com/VersionOne/loc-2.v1?Scope"
"Project"

Note the response is a bare JSON string, not an object. Prefer the array form even for one tag so your parsing stays uniform.

Edge Cases​

Query stringResponseNotes
?[Scope,Scope]one entryDuplicates collapse
?[]{}Empty array
?[Scope,,Timebox]"": ""An empty element resolves to an empty string, unresolved
? (nothing)""Empty query string parses as the empty string
?nullnullThe literal token null parses as JSON null

Quoting Rules You Will Eventually Hit​

LSON accepts 'single' or "double" quotes around a string, and treats an unquoted run of characters as a bare token that ends at whitespace, , or ]. Localization tags separate their terms with a tic ('), which interacts with that:

  • A tic inside a bare token is fine. [AttributeDefinition'Name'Story] parses as one tag, because quoting is only considered at the start of a value.
  • A tag that starts with a tic would be read as an opening quote. Quote it.
  • A tag containing a space, a comma or a ] must be quoted, or it will be split.

When in doubt, wrap every tag in double quotes and URL-encode the result:

Always-safe form
curl -G "https://www.example.com/VersionOne/loc-2.v1" \
--data-urlencode '=["AttributeDefinition'"'"'Name'"'"'Story","Plural'"'"'Story"]'

POST for Long Tag Lists​

A GET query string is subject to your web server's URL length limit. For large batches, POST the same LSON payload as the request body. The body is URL-decoded and then parsed identically.

POST form
curl -X POST "https://www.example.com/VersionOne/loc-2.v1" \
--data-urlencode '["Scope","Timebox","Story","Plural'"'"'Story"]'

POST responses are marked Cache-Control: no-cache.

Authentication​

loc-2.v1 does not require authentication. The handler overrides the authentication step to always succeed, so no credentials are needed and none are validated.

Sending an Authorization header anyway is harmless — it is simply ignored. Localization output is not user-specific; it depends only on the requested culture and the instance's configured terminology.

Choosing a Culture​

Set Accept-Language. Agility reads only the first entry of the header and converts it to a specific culture:

Accept-Language: fr-CA -> fr-CA
Accept-Language: fr -> fr-FR (neutral cultures are expanded to their default specific culture)
Accept-Language: xx-YY -> invariant culture (unparseable input is swallowed)
(header absent) -> invariant culture

Because a neutral culture is expanded before it reaches the localizer, you cannot address a neutral culture's templates directly over HTTP — you always land on a specific culture, which then falls back. What that fallback does is covered in Culture fallback.

Caching​

GET and HEAD responses are cacheable:

HeaderValue
Cache-Controlpublic, max-age=3600
Varyincludes Accept-Language
ETagderived from the newest write time in the strings folder

Send If-None-Match with a previously returned ETag and you get 304 Not Modified with an empty body. There is an important caveat about ETag staleness after a template change — see Hot reload and caching.

Errors​

StatusCause
405Any method other than GET, HEAD, POST or OPTIONS. Response carries Allow: GET,HEAD,POST,OPTIONS.
500The payload parsed, but an element was not a string — for example ?[1,2] or ?[{a:b}]. The message is Unsupported Type: <TypeName>.
500Any unexpected server-side failure.

OPTIONS returns 200 with the Allow header and no body.

Error bodies are text/plain, not JSON — the content type is reset before the error is written. Do not assume a JSON body on a non-2xx response from this endpoint.

There is no 400 for an unrecognised tag, because there is no such thing: an unmatched tag resolves to itself. See Tag resolution.

Legacy loc.v1​

~/loc.v1 predates loc-2.v1 and is still registered.

loc.v1loc-2.v1
MethodsGET, HEADGET, HEAD, POST
Payloadone raw tag as the whole query stringLSON scalar or array
Content typetext/plainapplication/json
Empty query string400 Missing TAG Parameter""
Legacy single-tag request
curl "https://www.example.com/VersionOne/loc.v1?AttributeDefinition'Name'Story"
Title

Both endpoints resolve tags through exactly the same localizer, so every semantic page in this section applies equally to both. Use loc-2.v1 for new work: one round trip for a whole screen's worth of labels beats one round trip per label.

Corrections to Older Documentation​

Older descriptions of this API are still in circulation. Where what you have read disagrees with this section, these are the corrections:

You may have readThe reality
The endpoint is loc.v1That is the legacy endpoint. loc-2.v1 exists and is the bulk form.
Responses are text/plainTrue of loc.v1. loc-2.v1 returns application/json.
Requests need an Authorization headerNo authentication is required or checked.
The override set is called CustomCustom is a sample application's set name. Agility's in-field override file is Company. See Template sets.