Payments are experiencing issues due to temporary restrictions in Russia. If your payment does not go through, please submit a support request.Our support team is available 24/7 — we are always here to help with hosting and server issues.We are now accepting requests for dedicated server rental and colocation services in our data center.Reminder: we recommend enabling backups for additional data protection.A new VPS/VDS lineup with NVMe storage and improved performance is now available.Maintenance work on some servers has been completed. All services are operating normally.
Article8 min readViews0

HTTP 200, but the order didn't appear: where API success ends

A green HTTP response confirms only one segment of the integration. We examine how to distinguish request acceptance, queuing, handler execution, and the actual order modification.

Comments 0

A glowing data capsule passes through several independent processing stages
In this article

The integration sent the order, received an HTTP 200, and logged "success." The accounting system shows no order. Who is right? Technically, both logs could be correct: the first confirms the API response, while the second reflects the actual data state. Between them may lie validation, a queue, a handler, and another transaction.

The main guideline is simple: give each confirmation a precise name. "Request accepted," "message saved to queue," "handler completed," and "order created with correct fields" are four distinct outcomes. If you label them all "success," the investigation usually begins with debating the green indicator. The indicator is not at fault; it was simply assigned too much responsibility.

One response confirms one segment

In a small example, a shop sends an order to an external system. The API checks the format, assigns an operation ID, and places the message in a queue. A separate handler reads it, matches the customer and items, then saves the document. The client receives the response before the final step completes.

In this scheme, an HTTP response describes the result of a conversation with the API but does not necessarily describe the future result of background work. The HTTP standard distinguishes several meanings: 200 indicates successful execution of the request according to the method contract; 201 indicates resource creation; 202 indicates acceptance for processing, which is not yet complete. For 202, the standard recommends describing the current state in the response and specifying a way to monitor the operation.

Therefore, a status code cannot be chosen as a decorative color. If the order has already been created and the response contains its identifier, the contract can report a completed operation. If only a task for a background processor is saved, the response must honestly reflect the incompleteness and return the operation identifier. If the system responds with 200 in any case, including an error in the body, the client must guess the meaning from the message string. Such a protocol works only until the first new error message appears.

Four levels of confirmation in API integration: HTTP response, queue, processor, and final order state
Each confirmation proves only its segment of the path. The final result of the operation is verified by the state of the business object.

The response contract is more important than the word "success"

The integration response must have a verifiable structure. It does not need to be large, but it must answer the client's questions without requiring a human to read a natural language phrase. For a synchronous operation, an identifier for the created or modified object is required. For an asynchronous operation, an operation identifier and a separate method to check its status are required. For a failure, a stable error type and details sufficient for resolution are required.

  • what result was achieved: completed, accepted for processing, or rejected;
  • which operation or entity was created and by which permanent identifier it can be found;
  • which version or original event was processed;
  • whether the request can be safely retried after a timeout;
  • where to view the final result and diagnostic information.

The HTTP status code provides general semantics, while the body clarifies the domain-specific result. For machine-readable errors, the Problem Details standard exists: it separates the error type, short title, status, detail, and instance. Using this exact format is not mandatory for every internal integration, but the principle is useful: a program must distinguish causes without searching for the word "error" in arbitrary text.

There is also the opposite extreme: always returning an error until the background process completes all work. In that case, a slow import holds the connection, the client does not know whether to wait further, and the intermediate server may terminate the request due to a timeout. Asynchronous processing itself is normal. The problem begins when an incomplete operation is presented as a completed business result.

A queue adds two more boundaries

The introduction of a queue makes the system more resilient to short spikes but introduces new confirmations. Using RabbitMQ as an example, the broker sends a confirmation to the message publisher: this means the broker has accepted responsibility for the message. The consumer confirmation belongs to a different area: the handler informs the broker that delivery has been processed. Official documentation explicitly separates these mechanisms—they are unaware of each other's results.

Even a consumer confirmation does not always prove a correct order. The handler may confirm the message too early, before the transaction is committed. It may save the document but skip a line due to incorrect mapping. It may write a status as "ready" even though the associated file did not upload. Transport reliability preserves the message; business validation confirms the meaning. These tasks are related but not interchangeable.

Almost every operation has at least three observable identifiers: the incoming request, the queue message, and the business object. It is convenient to link them with a single correlation ID without replacing it with the permanent order ID. This way, for a single operation, you can find the original request, processing attempts, and the final object, rather than matching logs by time with an accuracy of 'sometime after lunch'.

Retry after timeout is a separate test

The client sent an order and received no response: the connection dropped after ten seconds. It is impossible to determine whether the failure occurred before writing, after writing, or only during response delivery. The most natural next step is to retry the request. For a creation operation, this is the edge case that distinguishes a careful contract from a duplicate generator.

HTTP considers a method idempotent when multiple identical requests have the same intended effect as a single one. However, a standard POST does not become safe to retry just because the same JSON was sent. Integrations require their own durable operation key or an external order ID. On retry, the server must return the previously achieved result or continue the same operation, not create a neighboring document.

There is a catch: the repeatability key does not equal the hash of the entire body. The send time, field order, or service signature may change even if the business context remains the same. Conversely, the same key cannot be accepted with a different order composition silently. The contract must define the key retention period, the reaction to changed data, and the response for an already completed operation.

Correct requests may arrive in the wrong order

Let us add another condition. First, the store sends the order creation, then payment and address changes. The queue repeats a single message, two handlers work at different speeds, and the payment event reaches the recipient before the creation event. All requests are individually correct, but the outcome depends on the order.

The solution is chosen based on the data model. Sometimes the recipient delays the event until the base object appears. Sometimes the operation contains a version number, and the system rejects outdated changes. Sometimes the state is built from an event log where order is part of the contract. There is no universal flag, but there is a mandatory question during acceptance testing: what happens if events arrive again, with a delay, or in reverse sequence?

It is especially dangerous to silently treat an old event as successful. An HTTP response may be positive, the queue may be cleared, and no exceptions may occur—yet the buyer's address has reverted to a previous version. Therefore, the business result is verified not only by the existence of the order but also by significant fields, versions, and related entities.

Acceptance testing of the integration chain via confirmations

Acceptance testing does not require artificially triggering a failure on every server. It is sufficient to predefine expected outcomes and run safe scenarios in a test environment. The verification sequence might look like this:

  1. Send a minimal valid order and record the operation ID from the response.

  2. Verify that the response status matches the stage: the object is already created or the work has only been accepted.

  3. Find the final order by the permanent ID and compare the items, amounts, buyer, payment, and links.

  4. Repeat the same business operation after simulating an uncertain response and ensure that a second order did not appear.

  5. Submit two sequential changes in reverse order and verify the versioning rule or wait logic.

  6. Send invalid data and ensure that the rejection is distinguishable by the program, not just understandable to a human from free text.

  7. Link the API, queue, and handler logs with a single ID and verify that they contain no secrets or unnecessary personal data.

This check does not require promising delivery "exactly once." In a distributed system, it is more useful to design for repeatable processing and an observable outcome: a message may arrive again, but the order state will remain as defined by the contract.

What counts as success

API success ends where its contract ends. If the response only confirms task acceptance, it is not required to prove order creation—but it must honestly report the stage and provide a way to see the continuation. If a completed change is promised, the receiver must return the result identifier, and acceptance testing must verify the actual state.

A good integration does not try to close the entire flow with a single green status. It leaves a verifiable trail at every boundary: request, queue, handler, business object. In this case, a timeout indicates uncertainty in a specific segment, a retry does not create a duplicate, and an investigation starts with the operation ID, not with the question of whose log entry looks greener.

Discussion 0

Share your experience and ask questions. Comments without links appear after editorial review.

No comments yet. Start the discussion.