Using Bruno (the API Client) to play a.o. with Joomla Web Services
1 Introduction
In the past, many people were typically using Postman as API Client when testing Web Services / APIs.
More recently, many users have moved from Postman to Bruno. Here are the main reasons:
- Open-Source & Free: Bruno is an open-source alternative, providing a fast, lightweight tool that remains free for core functionalities, unlike Postman’s restrictive, tiered, and paid, feature-packed model.
- No Login/Cloud Required: Bruno allows immediate, unrestricted use without the need to create an account, log in, or worry about cloud-based synchronization.
- Local-First & Privacy-Focused: Unlike Postman, which stores data in the cloud, Bruno saves your API Collections, requests, and variables directly in a folder on your filesystem (using the .bru format). This ensures no data leaves your machine and works perfectly offline.
- Git-Friendly (Built-in Version Control): Because Bruno stores Collections as text files, you can use Git to version control, branch, and merge your API requests alongside your code, facilitating easy collaboration without proprietary tools.
- Lightweight Performance: As a desktop application, Bruno is faster and consumes less memory compared to the increasingly heavy, browser-based, or Electron-based, Postman interface.
- Simple & Focused: It specializes in API testing and development without the bloat of advanced, often unnecessary, features found in Postman.
The aim of this presentation is to get you started with Bruno.
It is a.o. inspired by a Session made by René Kreijveld at JoomlaCamp 26 on February 28th 2026 in Essen (Germany).
2 Resources
- This presentation is complementary to 2 other ones I made previously where you will find more links and more information about the Joomla Web Services (API)
- Playing with the Joomla Web Services (API) in Joomla 4 or 5 or 6
- Automation with Joomla (Web Services / API)
- Other presentations about Joomla Web Services made at Joomla events
- How to use the Joomla Web Services API? by Peter Martin aka Pe7er at #jd25nl & #jd22dach
- PhpStorm as alternative for Postman by Roland Dalmulder at #jd25nl
- Joomla Web Services (API)
- Documentation
- Collections (in historical order)
- https://github.com/alexandreelise/j4x-api-Collection
- historically the first Collection, released a few years ago
- by Alexandre Elisé
- format: Postman
- https://github.com/HermanPeeren/Joomla-OpenAPI
- since January 2026
- a complete OpenAPI specification of Joomla’s web services API
- by Herman Peeren
- format: OpenAPI
- https://github.com/renekreijveld/bruno-joomla-api
- since February 2026
- derived from Herman Peeren’s Collection
- by René Kreijveld
- format: Bruno
- and probably relatively soon: the Official Collection 😊
- https://github.com/alexandreelise/j4x-api-Collection
A big up respectively to Alexandre, Herman & René!
3 Installation
3.1 Install Bruno
Download and install Bruno from the official website: https://www.usebruno.com
Note: if you already have Bruno installed, make sure you update it to the latest version
3.2 Install the Collection
3.2.1 Download the Collection
- Go to https://github.com/renekreijveld/bruno-joomla-api
- Click on the green
<> Codebutton - Click on
Download ZIP - Save it on your computer
- Unzip it
- Put the
brunofolder and its content whereever you wish- for example at the root of your local website
- or simply in a more general folder

3.2.2 Open the Collection in Bruno
- open Bruno
- click on the
+sign next to Collections in the left sidebar - select
Open Collection

- navigate to the location where you copied the
brunofolder - open it
- click on the
Joomla Web Services APIfolder - and click the
Openbutton

- in the left sidebar below Collections you now see the Joomla Web Services API Collection
- click on it to expand and see all the folders and requests in the Collection

3.2.3 Configure your Collection in Bruno
- with the Collection open, at the top right you see a dropdown called
No environment - click on it to expand the dropdown and select
Joomla Web Services API - click on the button again to reopen the dropdown and click on
Configure

- it opens an
Environmentstab - there are two ready-to-use variables
base_url- adapt the URL with the URL of your Joomla website
- example:
https://www.myjoomlawebsite.com/api/index.php - note: be sure to keep the
/api/index.phppart at the end of the URL, as that is the endpoint for Joomla API requests
api_key- copy the API token from your Joomla User Profile
- ideally, you should create a new Super User instead of using your own Joomla User Profile so that
- you can easily disable it (security)
- you also see more clearly in the User Actions Logs what has been done via the backend vs via the Web Services
- the Web Services can also be made available to Users not having the
Super UserAccess Level but that is another topic
- ideally, you should create a new Super User instead of using your own Joomla User Profile so that
- paste it as the value of the
api_keyvariable
- copy the API token from your Joomla User Profile
- click the
Savebutton at the bottom of the Environments tab


⚠️ Security Note: Since Bruno stores these variables in plain text files (.bru), ensure you never commit your specific api_key to a public Git repository. Use .gitignore for your environment file or share keys only within trusted teams
4 Use your Collection in Bruno
Now it is time to start playing and to use the requests in the Collection to make API calls to your Joomla website.
For this tutorial, we will use René Kreijveld’s Bruno collection.
4.1 Terminology
Some basic terminology first when working with APIs. You are probably familiar with the acronym CRUD (Create / Read / Update / Delete). The corresponding requests are the following:
- Create =
post - Read =
get - Update =
patch - Delete =
del
4.2 A simple GET Request
When you make API calls a good start is always to run a simple GET request, especially if you have little experience (a POST request is not complicated, but there are already more chances to run into errors because you would forget some required Parameters, because you put a wrong Category ID, …).
- in the left sidebar click on Joomla Web Services API to expand the Collection
- click on
Content Articlesto see all possible requests on Articles - click on the the first option, ie
GET content/articles, which retrieves a List of Articles - send the request
- either by clicking on the
right arrowicon at the top right of your screen, next to the</>[Generate Code] and thefloppy disk[Save] icons - or by using the keyboard shortcut, ie
CTRL+ENTER
- either by clicking on the

If everything is fine, on the Response tab you should then see
- a green status
200 OK - and below the Response should contain the list of Articles of your Joomla website in
JSONformat (note that by clicking on theJSONbutton you could display the same Response in other formats likeHTML,XMLetc)

4.3 Using Parameters in Requests
On the Params tab, you see that we have a number of ready-to-use parameters
filterfor (according to the context for example) Author, Category, Language, …listfor Ordering and Direction
In the following example
- I check
filter[category]and put12as value (because I do have a Category having ID12of course) - I check
filter[search]and putStar Wars
When I run the request I then get the following result

4.4 Using Variables in Requests
Let’s now choose an another action, for example Content Articles > GET content/articles/{article_id}
On the newly opened Tab, note the URL which is mentioned after the GET: {{base_url}}/v1/content/articles/{{article_id}} where the Variable
{{base_url}}is green because we have already set a Value above{{article_id}}is red because we have not yet set any Value
Simply click on the red {{article_id}} and in the popup you can specify the Value of an existing Article (in my case: 212) and press Enter key

Note the {{article_id}} which was red has now become green

If you want to assign another value, simply
- click on
{{article_id}}again - or go on the Tab
Varswhere you see all Variables and where you can- either uncheck the current Value (
{{article_id}}will become red again) - either change its value
- either uncheck the current Value (

When I run the request I then get the following result

4.5 Making your Variables “global”
If for some reason you want to make a Variable “global”
- go to our Environment (where we already configured
base_urlandapi_key)- click on the
Joomla Web...button at the top right of Bruno - click on
Configurebutton
- click on the
- add a new line with for example
article_id(without the double curly brackets) asName- the desired
Value

4.6 A simple POST request
- click on
Content Articlesto see all possible requests on Articles - click on the the 2nd option, ie
POST content/articles
By default, the example Body is the following
{
"alias": "",
"articletext": "",
"catid": 0,
"language": "",
"metadesc": "",
"metakey": "",
"title": ""
}
But note that the only necessary fields are the following:
{
"articletext": "",
"catid": 2,
"language": "",
"title": "test post 2"
}
In particular
- must be present + must have a real Value
titlecatid
- must be present in the body but can be empty (ie with
""as Value)articletextlanguage. Note: when you want to put a value*stands forAllen-GBforEnglishfr-FRforFrenchetc (just like in your Joomla database)
Should you totally remove "articletext": "", from the Body then you would indeed get the following error:
{
"errors": [
{
"title": "Save failed with the following error: Field 'introtext' doesn't have a default value",
"code": 400
}
]
}
4.7 articletext vs introtext + fulltext
As you know, in Joomla
introtextstands for the text in the Editor before theRead morebuttonfulltextstands for the text in the Editor after theRead morebutton
Actually, articletext is “smart” and will separate your Value into introtext and fulltext when detecting <hr id="system-readmore"> in the Value.
So in the Body you can use the combination that suits you the most:
articletextaloneintrotextalone- both
introtext+fulltext - [ but not
fulltextwithoutintrotextbeing present. It can be empty though]
4.8 Basic Custom Fields
Suppose you have a Custom Field of Type Text, having
My Own Custom Fieldas Titlemy-own-custom-fieldas Name (Alias)
Then in the Body of the API Request you would simply add the following line in order to feed the Custom Field with the Value lorem ipsum:
"my-own-custom-field": "lorem ipsum",
4.10 Other Fields
Actually, if you need to add any Field (native Field or Custom Field) in a Post Request, the easiest way is to first do a Get Request in order to see
- what the
Labelis - and how the
Valueis formatted (example: dates are formatted likeyyyy-mm-dd hh:mm:ss)
The following example illustrates how to add Fields like
accesscreated_bystate- … and
associations(when you have a multilingual website you can indeed associate Articles between the different languages)
{
"title": "lorem ipsum",
"articletext": "",
"catid": 2,
"language": "en-GB",
"state": 1,
"access": 3,
"created_by": 100,
"associations": {
"nl-NL": "13",
"fr-FR": "14"
}
}
4.11 What if you POST an Article and the Alias is already taken in the given Category?
With J!4.x, J!5.x and J!6.0.x, you will get an error message if you POST an Article for which the Alias is already taken in the given Category. This can sometimes be annoying when you are automating Article creation.
But as of J!6.1 (release date: mid-April 2026) you won’t have to worry about that in your workflow because Joomla will automatically adapt the Alias. Example: if the Alias my-test-article-1 is already taken, it will give my-test-article-1.
Thanks to Nicola Galgano (aka @alikon) for improving this!
Want to know more about this? Check
- the Pull Request of Nicola
- and the corresponding Issue I had posted
4.12 A POST request to upload an image in a given folder
- click on
Media Filesto see all possible requests on Media - click on the the second option, ie
POST Add Media file - in the Body
- for the
Pathif you put for example/banners/joomlacamp26.pngthe media will be uploaded in the/banner/folder… under the/files/folderlocal-images:/banners/joomlacamp26.pngthe media will be uploaded in the/banner/folder… under the/images/folder
- for the
Contentyou should put your media file inbase64format. Example for our demo:- go to an online
base64converter like https://www.base64-image.de - upload your image
- click on the
</> Codebutton - copy the result… but without the initial
data:image/svg+xml;base64, - paste
- go to an online
- for the

4.12.1 Want to make a quick test?
Use the following Body where the Content is a tiny image:
{
"path": "local-images:/banners/tiny.png",
"content": "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII"
}
You will then see this tiny image in your (8x8 pixels ) /banners folder:

4.12.2 What if the file already exists?
If the file already exists when you POST you will get a 400 error:
{
"errors": [
{
"title": "File exists and overwriting not requested: /banners/joomlacamp26.png",
"code": 400
}
]
}
As explained by Herman Peeren:
A POST request only adds a new file, cannot overwrite an existing file. When doing a POST, the overridestate parameter of the model is set to false (in the add() method in the API Controller of com_media). If you want to overwrite an existing file, you’ll have to use a PATCH request (which sets the override state parameter of the model to true (in the edit() method in the controller). But in order to use that PATCH, the resource has to exist.
This is pretty much the same for all POST and PATCH requests, also for other resources. So you always get a sequence when adding a file:
- does the file already exist?
- if not, then do a POST.
- if it does exist, then do a PATCH.
But the same holds for adding an Article (Alias must not exist), Category (idem), User (Username and Email must not already exist), etc. So you always have this same sequence. One of the things an MCP-server offers on top of just the API is this sequence. And you’ll encounter it in all automation. And a step further: when creating an Article, you first have to check if the Category or User or whatever other entity is mentioned in the Article exists, and if not then create that other “dependent” resource first.
4.13 A DEL request to delete an Article
Similarly to the workflow when deleting an Article from the Backend, in order to delete an Article via the Web Services
- one must first Patch it with State on
-2(ieTrashed) in order to put it in Trash - and only then that Article can be really deleted
5 Not all Web Services are in Joomla yet
You might notice that this Collection has for example Guided Tours.
But at the moment (ie J!6.0) there is no Web Service for Guided Tours.
In order to see all the already available Web Services (endpoints) in Joomla
- go to the backend of your Joomla website
- click on System > Plugins
- filter on
Type=webservices
Note: in the available Web Services one could choose to enable only some of them.
6 Go further with Bruno
A few extra information before letting you play with Bruno.
Just to keep it short, let’s only mention the 3 buttons at the top right
- the
</>icon allows toGenerate Code, for example inPHP,Javascriptetc - the
🖫icon allows to Save the changes you made to a given item of the Collection. But alternatively, if you are playing and you want to keep the “original” intact, you could also simply click on the...icon next to the Action in order toCloneit - the
->icon allowing to execute the current request (keyboard shortcut:CTRL+ENTER)
Last but not least: in Bruno each item of the Collection a simple JSON file (with .bru extension), meaning for example that
- you can easily edit them in your
IDElikeVS CodeorPHPStormfor example - you have them available locally (unlike Postman)
- you can put them in a Git repo, work in team etc
7 Bruno directly in VS Code
With Bruno for VS Code extension, you can develop and test your APIs in Bruno right from Visual Studio Code. You can use the VS Code extension to open .bru files, send API requests, manage collections and environments, and much more. Streamline your development workflow by testing your APIs in the same application you use to develop them.
https://marketplace.visualstudio.com/items?itemName=bruno-api-client.bruno
8 Using directly PHPStorm as API Client
See this useful presentation made by Roland Dalmulder:
9 Summary
- download Bruno: https://www.usebruno.com
- clone Joomla’s Collection: https://github.com/renekreijveld/bruno-joomla-api
- configure the
base_urlandapi_key - start with a simple
GETRequest (reading is always easier than writing to start with) - continue with a
POSTrequest - finally contribute to the Collection and/or to Joomla! ❤️💙💚🧡
Note: this article is work in progress. Please contact me for any feedback/suggestion or if you know of any other interesting resource about this topic! I will be happy to integrate them in the current article which will also be used for presentations different Joomla events in order to spread the knowledge and share experiences.
Some articles published on the Joomla Community Magazine represent the personal opinion or experience of the Author on the specific topic and might not be aligned to the official position of the Joomla Project
By accepting you will be accessing a service provided by a third-party external to https://magazine.joomla.org/

Comments