Instructions for the Backend Tournament - 2025
Your team and/or you need to develop a backend that intermediates payment requests to a payment processing service, called Payment Processor.
For each intermediated payment, a financial fee is charged. For example, with a 5% fee for a $100.00 payment request, you would be charged $5.00 and keep $95.00.
However, since real life is tough, this service will face instabilities. Its response times may become very high and it may even become unavailable, responding with HTTP 500 errors. And since we know life is tough, we prepare for these things and create a plan B. The plan B is that there is a second Payment Processor service.
Note: The fees on payment processing will not change during the tests, and the default service will always have the lowest fee.
The problem is that this contingency service – called Payment Processor Fallback – charges a higher fee on payments. And it may also face instabilities and downtime! In fact, both services may be unstable and/or unavailable at the same time, because that’s how life goes…
Besides the POST /payments endpoint, it’s also necessary to provide an endpoint that details a summary of processed payments – GET /payments-summary. This endpoint will be used to audit consistency between what your backend processed and what the two Payment Processors processed. Think of it as the Central Bank checking if you’re recording everything properly from time to time.
These periodic calls during the Tournament test will compare the responses and, at every inconsistency, a hefty fine will be applied!
To make your life easier and to check the availability of the Payment Processors, each of them provides a health-check endpoint – GET /payments/service-health – that shows if the service is experiencing failures and what the minimum response time is for payment processing. However, this endpoint is limited to one call every five seconds. If this limit is exceeded, an HTTP 429 - Too Many Requests response will be returned. You may use these endpoints to develop the best strategy to pay the lowest fee possible.
The scoring criterion for the Backend Tournament will be how much profit your backend managed to achieve at the end of the test. In other words, the more payments you process with the lowest fee, the better. Keep in mind that if inconsistencies are detected by the Central Bank, you will have to pay a 35% fine on your total profit.
There is also a technical scoring criterion. If your backend and the Payment Processors have very fast response times, you can also score points. The metric used for performance will be the p99 (we’ll take the worst 1% response times – the 99th percentile). Starting at a p99 of 10ms or less, you receive a bonus on your total profit of 2% for each 1ms below 11ms.
The formula for the performance bonus percentage is (11 - p99) * 0.02. If the value is negative, the bonus is 0% – there is no penalty for results with p99 greater than 11ms.
Examples:
- p99 of 10ms = 2% bonus
- p99 of 9ms = 4% bonus
- p99 of 5ms = 12% bonus
- p99 of 1ms = 20% bonus
¹ The percentile will be calculated on top of all HTTP requests made during the test, not just the requests made to your backend.
² All payments will have exactly the same value – no random values will be generated.
Your backend must follow the following architecture/restrictions.
Web Servers: Have at least two instances of web servers that will respond to POST /payments and GET /payments-summary requests. That means some form of load distribution must occur (usually via a load balancer like nginx, for example).
Containerization: You must provide your backend in docker compose format. All images declared in docker compose (docker-compose.yml) must be publicly available in image registries (https://hub.docker.com/, for example).
You must restrict CPU and Memory usage to 1.5 CPU units and 350MB of memory across all declared services, as you prefer, using the attributes deploy.resources.limits.cpus and deploy.resources.limits.memory as in the example below:
services:
your-service:
...
deploy:
resources:
limits:
cpus: "0.15"
memory: "42MB"Examples of docker-compose.yml here, here, and here .
Port 9999: Your endpoints must be exposed on port 9999 accessible via http://localhost:9999 – example here .
- Images must be compatible with linux-amd64.
- The network mode must be bridge – host mode is not allowed.
- Privileged mode is not allowed.
- Use of replicated services is not allowed – this makes resource usage verification harder.
Important!: The deadline to submit your backend is 2025-08-17 at 23:59:59! Results are expected to be announced on 2025-08-20.
To have your backend officially tested by the Backend Tournament, see the results compared with other submissions, and have your name listed as a participant, you must do the following:
- Have a public git repository (GitHub, for example) with the source code and all artifacts related to the Tournament submission.
- Open a PR in this repository adding a directory with your identification in
participantes. In this PR you must:- Include a
README.mdexplaining the technologies used and a link to the repository with the source code of your submission. - Include the
docker-compose.ymlfile at the root of this repository with your dependencies (database scripts, configurations, etc.). - Include an
info.jsonfile with the following structure to facilitate the collection of used technologies:
- Include a
{
"name": "Débora Nis Zanfranceschi",
"social": ["https://x.com/debora-zan", "https://bsky.app/profile/debora-zan.bsky.social"],
"source-code-repo": "https://github.com/debora-zan/rinha-de-backend-2025",
"langs": ["node"],
"storages": ["postgresql", "redis"],
"messaging": ["rabbitmq", "nats"],
"load-balancers": ["nginx"],
"other-technologies": ["xpto"] // include anything that doesn't fit the other categories
}- Example of file structure of a submission PR:
├─ participantes/
| ├─ debs-node-01/
| | ├─ docker-compose.yml
| | ├─ info.json
| | ├─ nginx.config
| | ├─ sql/
| | | ├─ ddl.sql
| | | ├─ dml.sql
| | ├─ README.md
Important!
- Do not include source code in the submission.
- Do not include logs in the submission.
- Include ONLY what is necessary for test execution.
- Pay attention to CPU and memory restrictions.
The Payment Processors will also run in containers through the docker-compose.yml file (or docker-compose-arm64.yml for hosts that use ARM64 processors like newer MacOS models). The containerization files are in this directory.
It is important to note that your docker-compose.yml must declare the network used by the Payment Processors. This means you first need to bring up the Payment Processors so that their network is created before you bring up your backend. You will also need to include the payment-processor network in the services that will integrate with the Payment Processors, as in this example. Don’t forget to also declare the payment-processor network in your docker-compose.yml, as in this example.
After everything is configured in terms of networks, the two Payment Processors will be available at the following addresses for your services:
Payment Processor Default at http://payment-processor-default:8080
Payment Processor Fallback at http://payment-processor-fallback:8080
Now just reference these two addresses, as in this example.
These two addresses are also accessible via host so you can explore them at the following addresses:
Payment Processor Default at http://localhost:8001
Payment Processor Fallback at http://localhost:8002
Follow these instructions to test your backend locally.
leonardosegfault kindly wrote a mini setup guide to help with the local configuration to run the tests.
Main endpoint that receives payment requests to be processed.
POST /payments
{
"correlationId": "4a7901b8-7d26-4d9d-aa19-4dc1c7cf60b3",
"amount": 19.90
}
HTTP 2XX
Anythingrequest
correlationId is a required and unique field of type UUID.
amount is a required field of type decimal.
response
Any response in the 2XX range (200, 201, 202, etc.) is valid. The response body will not be validated – it can be anything or even empty.
This endpoint must return a summary of what has already been processed in terms of payments.
GET /payments-summary?from=2020-07-10T12:34:56.000Z&to=2020-07-10T12:35:56.000Z
HTTP 200 - Ok
{
"default" : {
"totalRequests": 43236,
"totalAmount": 415542345.98
},
"fallback" : {
"totalRequests": 423545,
"totalAmount": 329347.34
}
}request
from is an optional timestamp field in ISO format in UTC (usually 3 hours ahead of Brazil’s time).
to is an optional timestamp field in ISO format in UTC.
default.totalRequests is a required integer field.
default.totalAmount is a required decimal field.
fallback.totalRequests is a required integer field.
fallback.totalAmount is a required decimal field.
Important! This endpoint, together with Payments Summary from the Payment Processors, will be called several times during the test for consistency verification. The values must be consistent; otherwise, there will be penalties for inconsistency.
Your backend must integrate with two Payment Processors. Both services have identical APIs, so the following descriptions apply to both.
This endpoint receives and processes a payment – it is similar to the Payments endpoint your backend must provide. It’s the main endpoint for integration.
POST /payments
{
"correlationId": "4a7901b8-7d26-4d9d-aa19-4dc1c7cf60b3",
"amount": 19.90,
"requestedAt" : "2025-07-15T12:34:56.000Z"
}
HTTP 200 - Ok
{
"message": "payment processed successfully"
}request
correlationId is a required and unique field of type UUID.
amount is a required field of type decimal.
requestedAt is a required timestamp field in ISO format in UTC.
response
message is an always-present field of type string.
This endpoint allows you to check the conditions of the Payments endpoint. This endpoint in both Payment Processors may help you decide the best option for processing a payment.
GET /payments/service-health
HTTP 200 - Ok
{
"failing": false,
"minResponseTime": 100
}request
No request parameters. However, this endpoint enforces a call limit – 1 call every 5 seconds. If exceeded, you will receive an HTTP 429 - Too Many Requests error response.
response
failing is an always-present boolean field indicating whether the Payments endpoint is available. If not, requests to the endpoint will return HTTP5XX errors.
minResponseTime is an always-present integer field indicating the best possible response time for the Payments endpoint. For example, if the returned value is 100, there will be no responses faster than 100ms.
You don’t need to integrate with this endpoint. It’s for troubleshooting, if you want/need it.
GET /payments/{id}
HTTP 200 - Ok
{
"correlationId": "4a7901b8-7d26-4d9d-aa19-4dc1c7cf60b3",
"amount": 19.90,
"requestedAt" : 2025-07-15T12:34:56.000Z
}request
{id} is a required UUID parameter.
response
correlationId is an always-present UUID field.
amount is an always-present decimal field.
requestedAt is an always-present timestamp field in ISO format in UTC.
The Payment Processor services include administrative endpoints. These endpoints will be used during the test BY THE TEST SCRIPT and you should not integrate with them in the final version. However, they can be useful to simulate failures, slow response times, verify consistency, etc. All the following endpoints require a token provided in the X-Rinha-Token request header.
This endpoint is similar to the Payments Summary endpoint you must develop in your backend.
GET /admin/payments-summary?from=2020-07-10T12:34:56.000Z&to=2020-07-10T12:35:56.000Z
HTTP 200 - Ok
{
"totalRequests": 43236,
"totalAmount": 415542345.98,
"totalFee": 415542.98,
"feePerTransaction": 0.01
}request
from is an optional timestamp field in ISO format in UTC.
to is an optional timestamp field in ISO format in UTC.
response
totalRequests is an always-present integer field. Shows how many payments were processed in the selected period or all payments if no period is provided.
totalAmount is an always-present decimal field. Shows the sum of all payments processed in the selected period or the sum of all payments if no period is provided.
totalFee is an always-present decimal field. Shows the sum of fees for payments processed in the selected period or all payments if no period is provided.
feePerTransaction is an always-present decimal field. Shows the fee amount per transaction.
Important! This endpoint, together with the Payments Summary your backend must provide, will be called several times during the test for consistency verification. The values must be consistent; otherwise, there will be penalties for inconsistency.
This endpoint sets a password for the administrative endpoints. If you change the password in your final submission, the test will be aborted and you will score zero in the Tournament. The initial password is 123 and you can use it for local tests.
PUT /admin/configurations/token
{
"token" : "any password"
}
HTTP 204 - No Contentrequest
token is a required string field.
response
N/A
This endpoint configures an intentional delay on the Payments endpoint to simulate a longer response time.
PUT /admin/configurations/delay
{
"delay" : 235
}
HTTP 204 - No Contentrequest
delay is a required integer field that defines the milliseconds of response delay in the Payments endpoint.
response
N/A
This endpoint configures an intentional failure on the Payments endpoint to simulate server errors.
PUT /admin/configurations/failure
{
"failure" : true
}
HTTP 204 - No Contentrequest
failure is a required boolean field that defines whether the Payments endpoint will return a failure.
response
N/A
This endpoint deletes all payments from the database and is only for development convenience.
POST /admin/purge-payments
HTTP 200 - Ok
{
"message": "All payments purged."
}request
N/A
response
message is an always-present string field.
The tables below provide a summary to facilitate an overview of the solution.
Endpoints to be developed
| Endpoint | Description |
|---|---|
| POST /payments | Intermediates the request for payment processing. |
| GET /payments-summary | Displays details of payment processing requests. |
Endpoints available in both Payment Processor services
| Endpoint | Description |
|---|---|
| POST /payments | Requests payment processing. |
| GET /payments/service-health | Checks the health of the payment endpoint. Limit 1 call every 5 seconds. |
| GET /payments/{id} | Displays details of a payment processing request. |
| GET /admin/payments-summary | Displays details of payment processing requests. |
| PUT /admin/configurations/token | Resets an access token required for all endpoints prefixed with '/admin/' |
| PUT /admin/configurations/delay | Configures the delay on the payments endpoint. |
| PUT /admin/configurations/failure | Configures failure on the payments endpoint. |
| POST /admin/purge-payments | Deletes all payments from the database. For development purposes only. |
- The test server specifications can be found here.
- The Payment Processor source code is available here. - Very good and educational video about how to automate the publication of Docker images with GitHub Actions made by Emilio Heinzmann.