API v1 to v2 migration guide
Ticketing API Client v1 to v2 Migration Guide
This guide helps consumers of the Ticketing API client libraries migrate from the v1 generation of the Java and Go clients to v2.
What “v1” and “v2” mean here. Both client generations call the same HTTP API, served under
https://ticketing.masstack.com/v2(production) andhttps://ticketing.sta.masstack.com/v2(staging). The version in the library path refers to the generated client, not to a different HTTP base path: v1 clients were generated from OpenAPI document 2.1.1, v2 clients from 2.2.0. If you call the API over HTTP without the libraries, only the HTTP-level changes section applies to you.
At a glance
| Topic | v1 clients | v2 clients |
|---|---|---|
| Java Bazel target | //pkg/mas-stack/oss/mas-ticketing/client/java/v1:api-client | //pkg/mas-stack/oss/mas-ticketing/client/java/v2:api-client |
| Go Bazel target | //pkg/mas-stack/oss/mas-ticketing/client/go/v1:go_default_library | //pkg/mas-stack/oss/mas-ticketing/client/go/v2:go_default_library |
| Go import path | github.com/masmovil/mm-monorepo/pkg/mas-stack/oss/mas-ticketing/client/go-go-v1 | github.com/masmovil/mm-monorepo/pkg/mas-stack/oss/mas-ticketing/client/go-go-v2 |
| OpenAPI document | 2.1.1 | 2.2.0 |
| Transition custom fields | Placeholder properties customField1, customField2, customFieldN | Single customFields object keyed by Jira custom field name |
ticketing-provider values | JAM, JCC, TGJ, JNEXT | JAM, TGJ, JNEXT, JNODUS, OCEANE |
| Go service names | TicketApi, CommentApi, AttachmentApi, IssueLinkApi, IssueLinkTypesApi | TicketAPI, CommentAPI, AttachmentAPI, IssueLinkAPI, IssueLinkTypesAPI, plus RemotelinkAPI, GridAPI, IaSummaryAPI |
| Java JSON serialization | Gson (Retrofit GsonConverterFactory) | Gson (unchanged) |
Breaking changes
1. Custom fields in transitions
The request body of POST /orgs/{org}/ticket/{ticketId}/transitions changed shape. The v1 Transition model could not express real custom fields: it only exposed placeholder properties (customField1, customField2, customFieldN), so integrations that needed to set custom fields during a transition typically bypassed the typed model and sent them as top-level properties of a hand-built JSON body.
In v2 custom fields travel in a dedicated customFields object, keyed by the actual Jira custom field name (for example external_system_name, external_system_info, solution_detail). Only the fields allowed by the specific transition are processed.
Legacy layout (v1 era, top-level fields):
{
"transitionId": 111,
"external_system_name": "Orange",
"other_custom_field": "value"
}v2 layout (required):
{
"transitionId": 111,
"customFields": {
"external_system_name": "Orange",
"other_custom_field": "value"
}
}2. Ticketing providers
The ticketing-provider header keeps its name in both generations, but its allowed values changed:
JCC(Jira CC) was removed. The provider is decommissioned; requests using it will not be routed anywhere.JNODUS(Jira Nodus) andOCEANE(Oceane) were added.
If your code hard-codes provider values, review them against the current list: JAM, TGJ, JNEXT, JNODUS, OCEANE.
3. Go: service names and import path
The v2 Go client was generated with a newer generator that names services with an uppercase API suffix: client.TicketApi becomes client.TicketAPI, client.CommentApi becomes client.CommentAPI, and so on. The import path also changes (see the table above). Both changes are caught by the compiler, so they are easy to find but must be fixed in every call site.
4. New operations (non-breaking)
The v2 clients expose operations that did not exist in v1: remote links (RemotelinkApi / RemotelinkAPI, already present in the Java v1 client but new for Go), grid (GridApi / GridAPI) and IA summary (IaSummaryApi / IaSummaryAPI). Nothing to migrate; they are simply available once you move to v2.
Java serialization
Both Java generations are Retrofit 2 + RxJava 3 clients that serialize with Gson through GsonConverterFactory (see com.masmovil.ticketing.client.JSON and ApiClient). There is no serializer change to account for. The only serialization-related difference is the Transition model itself: customFields is declared as Object and accepts any Map<String, Object>, which Gson serializes as a nested JSON object.
Migration steps
Java client
1. Update the dependency
# Old
deps = [
"//pkg/mas-stack/oss/mas-ticketing/client/java/v1:api-client",
]
# New
deps = [
"//pkg/mas-stack/oss/mas-ticketing/client/java/v2:api-client",
]2. Imports
Package names are unchanged (com.masmovil.ticketing.client, com.masmovil.ticketing.client.api, com.masmovil.ticketing.client.model), so existing imports keep compiling. Re-run your build to make sure no class you used disappeared or changed signature.
3. Update transition calls
The doTransition signature is the same in both generations:
Observable<Response<Void>> doTransition(
String ticketId,
String org,
Transition transition,
Map<String, String> headers, // must include ticketing-provider
Map<String, String> queryParams);What changes is how you build the body. With v1 there was no typed way to send custom fields; with v2 they go into Transition.customFields:
ApiClient apiClient = new ApiClient(); // configure base URL and auth as you do today
TicketApi api = apiClient.createService(TicketApi.class);
Map<String, Object> customFields = new HashMap<>();
customFields.put("external_system_name", "Orange");
customFields.put("external_system_info", "Escalated via API");
Transition transition = new Transition()
.transitionId(111)
.customFields(customFields);
Map<String, String> headers = Map.of("ticketing-provider", "TGJ");
api.doTransition(ticketId, org, transition, headers, Collections.emptyMap())
.subscribe(
response -> {
if (!response.isSuccessful()) {
// handle 4xx/5xx: response.code(), response.errorBody()
}
},
error -> {
// handle transport errors
});Note the parameter order: ticketId comes before org.
The client returns an RxJava 3 Observable, the same reactive types Vert.x Rx uses, so from a verticle you stay reactive: chain or subscribe as above, and let OkHttp run the request on its own threads. Do not call blockingFirst() or any other blocking* operator on the event loop — it stalls the loop and Vert.x will report a blocked thread. Reserve blocking only for plain, non-reactive code (a CLI, a batch job):
Response<Void> response = api
.doTransition(ticketId, org, transition, headers, Collections.emptyMap())
.blockingFirst(); // only outside an event loopGo client
1. Update the dependency
# Old
deps = [
"//pkg/mas-stack/oss/mas-ticketing/client/go/v1:go_default_library",
]
# New
deps = [
"//pkg/mas-stack/oss/mas-ticketing/client/go/v2:go_default_library",
]2. Update the import path
// Old
import ticketing "github.com/masmovil/mm-monorepo/pkg/mas-stack/oss/mas-ticketing/client/go-go-v1"
// New
import ticketing "github.com/masmovil/mm-monorepo/pkg/mas-stack/oss/mas-ticketing/client/go-go-v2"The Go package name is mas_ticketing_client in both generations; aliasing the import (as above) keeps call sites stable.
3. Rename services and update transition calls
client := ticketing.NewAPIClient(cfg)
transition := ticketing.Transition{
TransitionId: 111, // int32, required (not a pointer)
CustomFields: map[string]interface{}{
"external_system_name": "Orange",
"external_system_info": "Escalated via API",
},
}
// v1 was client.TicketApi; v2 is client.TicketAPI
resp, err := client.TicketAPI.
DoTransition(ctx, ticketId, org).
TicketingProvider([]string{"TGJ"}).
Transition(transition).
Execute()Execute() returns (*http.Response, error); the transition endpoint has no response body.
HTTP-level changes
If you integrate directly over HTTP, this is the whole migration:
- Send transition custom fields inside the
customFieldsobject (see Custom fields in transitions) instead of as top-level properties. - Stop using
JCCasticketing-provider; use one ofJAM,TGJ,JNEXT,JNODUS,OCEANE. - Base URLs, authentication (
Authorization: Bearer {token}) and the rest of the paths are unchanged.
The current contract is published on the developer portal (Ticketing API v2) and, for internal users, in the IDP catalog (ticketing-api-v2, Definition tab).
Testing your migration
Add a serialization test so a future refactor cannot silently fall back to the legacy layout. Test with the same serializer the client uses at runtime.
Java (Gson, as used by the client):
@Test
void transitionSerializesCustomFieldsNested() {
Map<String, Object> customFields = new LinkedHashMap<>();
customFields.put("external_system_name", "Orange");
Transition transition = new Transition().transitionId(111).customFields(customFields);
Gson gson = new JSON().getGson();
JsonObject root = gson.toJsonTree(transition).getAsJsonObject();
assertTrue(root.has("customFields"));
assertFalse(root.has("external_system_name"));
assertEquals("Orange", root.getAsJsonObject("customFields").get("external_system_name").getAsString());
}Go (encoding/json, as used by the client):
func TestTransitionSerializesCustomFieldsNested(t *testing.T) {
transition := ticketing.Transition{
TransitionId: 111,
CustomFields: map[string]interface{}{"external_system_name": "Orange"},
}
payload, err := json.Marshal(transition)
require.NoError(t, err)
var parsed map[string]interface{}
require.NoError(t, json.Unmarshal(payload, &parsed))
require.NotContains(t, parsed, "external_system_name")
customFields := parsed["customFields"].(map[string]interface{})
assert.Equal(t, "Orange", customFields["external_system_name"])
}The v2 Java client ships equivalent tests (TransitionSerializationTest, TicketApiDoTransitionContractTest) that you can use as a reference.
Compatibility period
- v1 clients remain in the repository but are deprecated: use them only for code you are not touching yet. They will not receive the new operations nor the updated provider list.
- v2 clients are the target for all new development.
- Backend: the API currently still accepts the legacy top-level layout for transition custom fields, but that tolerance is temporary and will be removed. Migrate to
customFieldsnow rather than when it breaks.
Common migration issues
Custom fields are not applied in Jira
Symptom: the transition succeeds but the custom fields you sent are missing.
Cause: the fields were sent as top-level properties (legacy layout), or the field is not allowed by that specific transition.
Fix: put them inside customFields, using the Jira custom field names. Check which fields a transition accepts with GET /ticket/{ticketId}/metadata/update/properties before assuming a field can be set.
Go: client.TicketApi undefined
Symptom: compile error after switching the import path.
Cause: services were renamed to the ...API suffix in v2.
Fix: rename every service reference (TicketApi → TicketAPI, CommentApi → CommentAPI, etc.).
Requests rejected or unrouted after migrating
Symptom: errors about an invalid or unsupported ticketing-provider value.
Cause: the provider value JCC is no longer valid.
Fix: use the current provider list; if your tickets lived in Jira CC, contact the mas-ticketing team to confirm where they were migrated.
Existing tests fail after migrating
Symptom: JSON assertions written against the legacy layout fail.
Cause: expectations still look for top-level custom fields.
Fix: update them to the nested customFields layout, as in the examples above.
Rollback plan
If you hit a blocking issue:
- Revert the Bazel dependency (and, for Go, the import path and service names) to v1.
- Report the problem to the mas-ticketing team with the error message, a minimal code snippet and the environment (dev/sta/prod).
- Keep your v2 changes in a branch so they can be re-applied once the issue is resolved.
Need help?
- Slack: #mas-ticketing
- Jira: open an issue in the TICTEC project
- API contract: Ticketing API v2 on the developer portal
- Internal documentation: mas-ticketing TechDocs and the ticketing-api-v2 catalog entity
Additional resources
- V2 client testing and migration guide (maintainers) — how the client libraries themselves are tested and regenerated
- ADR-002: Versioning Strategy — why client generations are versioned this way