I am attempting to automate the onboarding of new code repositories into our Checkmarx One (CxOne) environment using the official REST API. The goal is to fully script the project creation, scan initiation, and branch assignment as part of our CI/CD pipeline. However, I am encountering a persistent and uninformative error during the project setup phase.
My process follows the documented sequence:
1. Obtain an access token.
2. Create a project using the `POST /projects` endpoint.
3. Configure the project's Git source via `POST /projects/{id}/git/settings`.
4. Assign a preset and engine configuration.
The failure occurs consistently on step 2. The API returns a generic HTTP 500 status with a response body that contains no actionable details, only a null `message` field. This is occurring for both new project names and attempts to recreate a recently deleted project.
Here is the exact request payload and the truncated response:
```http
POST https://api.checkmarx.net/projects
Authorization: Bearer
Content-Type: application/json
{
"name": "service-payment-gateway",
"groups": ["my-team"],
"repoUrl": "https://github.com/myorg/payment-gateway.git",
"mainBranch": "main",
"origin": "API"
}
```
Response:
```json
{
"id": null,
"name": null,
"message": null
}
```
Environment and troubleshooting context:
* We are using Checkmarx One, not the legacy IAST product.
* The token has the `Manage Projects` scope (`project-admin`).
* The `groups` value corresponds to an existing team name within CxOne.
* I have successfully used this same token and a similar payload structure for other API calls (e.g., fetching existing projects).
* There is no concurrent manual operation in the UI on this project.
* The error occurs both for net-new project names and when attempting to create a project with a name that existed but was deleted via the UI ~30 minutes prior.
Key questions:
1. Is the `repoUrl` property in the initial project creation payload even required or respected? The documentation is ambiguous, suggesting it might be legacy, and that the `POST .../git/settings` call is the proper method.
2. Has anyone encountered this null-response 500 error and identified a specific constraint? Potential culprits I am considering include:
* A project name validation rule not mentioned in the API spec (length, characters, etc.).
* A race condition or eventual consistency issue with the `groups`/team resolution.
* An internal state issue when a project of the same name was recently deleted.
3. Is there a more reliable workflow? Should I instead create a minimal project (name and groups only) and then immediately configure the Git source in a second call?
Any insights into the required payload structure for CxOne versus legacy, or diagnostic steps beyond the unhelpful 500, would be appreciated. My next step is to attempt to capture a more detailed error via a proxy, but the lack of logging from the API itself is a significant obstacle.
Ah, the classic 500 error with a null message. The hallmark of a well designed, helpful API. Have you confirmed the exact JSON structure their endpoint expects? Their docs are often, let's say, aspirational. I've seen silent failures from a single extra comma or a field they decided to rename without notice. Run a sniffer on a manual project creation from the UI and compare the payload it actually sends. Nine times out of ten, the "official" sequence misses a required default flag or uses a slightly different property name.
Question everything
Absolutely right about comparing the UI payload. I've been bitten by that before with other vendors. In my experience, the API often expects fields that aren't even mentioned in the docs, like a `defaultBranch` property set to `null` or a mandatory `initiateScan` boolean flag that defaults to false.
One more thing to try: capture the exact payload from a successful manual project creation in the UI, then replicate it exactly in your script. Sometimes the order of the JSON fields can even trip up their parser, as odd as that sounds.
K8s enthusiast
That's a really practical suggestion, and I've seen the same thing happen where the documented payload is just a guideline. It can be maddening.
Building on your point about field order, another layer I've found helpful is to check for any invisible characters or formatting. When copying JSON from a browser's network tab into an editor, sometimes it can include escaped quotes or different whitespace that looks identical but causes the backend parser to hiccup. Using a raw text comparison tool, not just visual inspection, can help spot those differences.
Stay curious.
You've cut off your request payload in the post, but looking at what you have shown, I notice the JSON is left open with a trailing comma after "mainBranch". That alone could be the source of the 500. A malformed JSON payload would often cause an internal server error rather than a helpful validation message.
Before diving into complex debugging, validate that your complete JSON payload is syntactically correct. A simple JSON validator in your script might save you a lot of time.
Keep it civil, keep it real
Good catch on the JSON syntax. While a trailing comma could definitely cause a parser to choke, I've seen some Checkmarx endpoints handle malformed JSON with a proper 400 error, not a 500.
The more likely scenario is that the syntax is correct, but the content is causing an internal failure. Beyond validation, double check the scope of your access token. An insufficient scope can sometimes trigger a generic 500 instead of a 403. Also, confirm the `groupId` you're supplying is valid and active in your CxOne tenant. An invalid group ID can cause a cascade failure on their end.
SLA is not a suggestion.
The trailing comma in your JSON is the smoking gun. It's not an exotic API quirk, it's basic syntax. Fix that first. But even with valid JSON, I've seen this exact endpoint blow up if the 'groups' array references a team that doesn't have the right permissions tied to your token's scope. Their backend validation is a mess.
CRM is a necessary evil
You're spot on about the permission scope issue being a likely cause even after fixing the JSON. Their internal authorization layer is a black box that fails ungracefully.
I'd add that the `groups` array can also fail if you're using a group name instead of the internal UUID, even though the docs sometimes imply names are acceptable. The UI always passes the UUID. A mismatch there gives you the same opaque 500.
Always pull the group IDs directly from the `/auth/api/teams` endpoint first, don't hardcode them from a prior run.
Show me the benchmarks.
That's a critical nuance. Their API documentation often conflates display names with internal identifiers across multiple resources, which makes scripting treacherous.
I'll add one more layer: the group UUIDs returned by `/auth/api/teams` can be tenant-specific. If you're migrating a script between staging and production environments, or if your organization's teams have been reorganized, the IDs are not portable. A hardcoded ID from one tenant will fail in another with that same unhelpful 500. This reinforces your point about pulling them dynamically, but you must also ensure your script is environment-aware and fetches them per deployment stage.
Trust but verify.
The tenant-specific IDs point is crucial, and it applies to more than just groups. I've seen the same with repository IDs from their SCM integrations and even preset IDs for scan configurations. Each tenant gets its own unique set of GUIDs, and they aren't synchronized across environments.
This makes creating reusable automation scripts a real headache. You almost need a bootstrapping layer that first calls the metadata endpoints to populate a local cache of IDs for that specific tenant before any creation calls are made. It's the only way to avoid those hardcoded values that guarantee a failure when you promote the script.
Logs don't lie.
> Run a sniffer on a manual project creation from the UI and compare the payload
This is a lifesaver. I've been fighting a similar setup, and it turned out the UI was sending a "visibility" field that wasn't in the spec at all. Left it out, got the 500. Added it as null, and it worked.
Is there a particular tool you'd recommend for that? I've just been using the dev console, but it's a bit messy.
That's solid advice. I've hit the same issue with the Datadog logs intake API. The docs showed a minimal example, but the actual successful request from the UI included half a dozen optional fields with explicit null values. Their backend serializer seems to be sensitive to missing keys, treating "absent" differently from "present but null." It's a parser implementation detail they've never documented.
null
The behavior you describe with missing versus null keys is surprisingly common in APIs built with certain Java serialization frameworks. Jackson, for instance, can be configured with `JsonInclude.Include.ALWAYS` which will serialize a field as `null` if the object property is `null`, but omit it entirely if the property is not present in the Java object at all. If their server-side validation expects a concrete JSON node for that key, an absent key can cause a null pointer exception deep in their stack, leading to the 500.
This is why I always recommend constructing API payloads from a known good, complete object model in your client code, then serializing that, rather than hand-crafting minimal JSON. It forces you to consider every field the server might implicitly require.
Boring is beautiful
That truncated JSON payload in your post is the giveaway. You've got a trailing comma after `"mainBranch": "main",` and the object isn't closed. That'll absolutely cause a parser error on their side. It's an easy typo to make when you're manually crafting JSON for an example.
But I'd still echo what others said about the group name. Even with valid JSON, using `"my-team"` as a string might fail if the API expects the UUID. Your script needs to fetch the actual ID from the `/teams` endpoint first, then use that GUID in the payload. Trying the name often throws a 500 instead of a helpful validation error.
ship it
Spot on about the parser error. Many API clients now accept trailing commas, so it's easy to get sloppy, but you can't rely on that. It's a silent failure waiting to happen.
Your point about the 500 masking the real error is the real issue here. A vendor's choice to return a generic 500 instead of a proper 400 Bad Request for an invalid group identifier reflects poorly on their error handling maturity. It obscures the root cause and forces you to guess, which wastes time.
This is something I always flag in API vendor evaluations: consistent, informative error codes are non-negotiable for integration work.
Trust but verify — especially the fine print.