Commit 7e6ed2c6 authored by Victor Kiprotich's avatar Victor Kiprotich

initial commit

parents
Pipeline #658 canceled with stages
# Znuny 6.5 – REST API (CustomerUser & Ticket) Guide
This document consolidates:
-**Session-based auth** (SessionCreate/Get/Delete)
-**CustomerUser** operations (Create/Get/Update/Search)
-**Ticket** operations (Create/Get/Update/Search/History)
-**Attachment** upload & download
-**YAML configuration** for a single webservice exposing both domains
-**Mermaid sequence diagrams** showing full request/response flows
-**Copy‑pasteable cURL examples**
---
## 1) Webservice YAML (single file)
> Save as `/opt/otrs/znuny_portal.yml` (or any name you prefer). Then run:
>
> ```bash
> /opt/otrs/bin/otrs.Console.pl Admin::WebService::Add \
> --name znuny_portal \
> --source-path /opt/otrs/znuny_portal.yml
> ```
```yaml
---
Debugger:
DebugThreshold: error
TestMode: '0'
Description: "Portal REST for CustomerUser & Ticket (Znuny 6.5)"
Provider:
Operation:
# ========== Session ==========
SessionCreate:
Description: Create a session (returns SessionID)
MappingInbound: {}
MappingOutbound: {}
Type: Session::SessionCreate
SessionGet:
Description: Get session info
MappingInbound: {}
MappingOutbound: {}
Type: Session::SessionGet
SessionDelete:
Description: Delete a session
Type: Session::SessionRemove
# ========== CustomerUser ==========
CustomerUserCreate:
Description: Create customer user
MappingInbound: {}
MappingOutbound: {}
Type: CustomerUser::CustomerUserCreate
CustomerUserGet:
Description: Get a customer user by login
MappingInbound: {}
MappingOutbound: {}
Type: CustomerUser::CustomerUserGet
CustomerUserUpdate:
Description: Update customer user
MappingInbound: {}
MappingOutbound: {}
Type: CustomerUser::CustomerUserUpdate
CustomerUserSearch:
Description: Search customer users (by PostMasterSearch/email or generic fields)
MappingInbound: {}
MappingOutbound: {}
Type: CustomerUser::CustomerUserSearch
# ========== Ticket core ==========
TicketCreate:
Description: Create ticket (with optional first article & attachments)
MappingInbound: {}
MappingOutbound: {}
Type: Ticket::TicketCreate
TicketGet:
Description: Get base ticket info
MappingInbound: {}
MappingOutbound: {}
Type: Ticket::TicketGet
TicketUpdate:
Description: Update ticket (and add note article/attachments)
MappingInbound: {}
MappingOutbound: {}
Type: Ticket::TicketUpdate
TicketSearch:
Description: Search tickets
MappingInbound: {}
MappingOutbound: {}
Type: Ticket::TicketSearch
TicketHistoryGet:
Description: Get ticket history
MappingInbound: {}
MappingOutbound: {}
Type: Ticket::TicketHistoryGet
# ========== Attachments (download only; upload happens via Article.Attachment array) ==========
ArticleAttachmentGet:
Description: Download a specific attachment by ArticleID & AttachmentID
MappingInbound: {}
MappingOutbound: {}
Type: Ticket::ArticleAttachmentGet
Transport:
Type: HTTP::REST
Config:
KeepAlive: '1'
MaxLength: '100000000'
AdditionalHeaders: ~
RouteOperationMapping:
# ----- Session -----
SessionCreate:
ParserBackend: JSON
RequestMethod: [ POST ]
Route: /Session
SessionGet:
ParserBackend: JSON
RequestMethod: [ GET ]
Route: /Session/:SessionID
SessionDelete:
ParserBackend: JSON
RequestMethod: [ DELETE ]
Route: /Session/:SessionID
# ----- CustomerUser -----
CustomerUserCreate:
ParserBackend: JSON
RequestMethod: [ POST ]
Route: /CustomerUser
CustomerUserGet:
ParserBackend: JSON
RequestMethod: [ POST ]
Route: /CustomerUser/Get
CustomerUserUpdate:
ParserBackend: JSON
RequestMethod: [ POST ]
Route: /CustomerUser/Update
CustomerUserSearch:
ParserBackend: JSON
RequestMethod: [ POST ]
Route: /CustomerUser/Search
# ----- Ticket -----
TicketCreate:
ParserBackend: JSON
RequestMethod: [ POST ]
Route: /Ticket
TicketGet:
ParserBackend: JSON
RequestMethod: [ GET ]
# NOTE: SessionID must be passed as a query parameter (?SessionID=...)
Route: /Ticket/:TicketID
TicketUpdate:
ParserBackend: JSON
RequestMethod: [ PATCH ]
Route: /Ticket/:TicketID
TicketSearch:
ParserBackend: JSON
RequestMethod: [ POST ]
Route: /Ticket/Search
TicketHistoryGet:
ParserBackend: JSON
RequestMethod: [ GET ]
# NOTE: SessionID must be passed as a query parameter (?SessionID=...)
Route: /Ticket/History/:TicketID
# ----- Attachment download -----
ArticleAttachmentGet:
ParserBackend: JSON
RequestMethod: [ GET ]
# NOTE: returns JSON { Filename, ContentType, Content } where Content is base64
Route: /Ticket/Article/:ArticleID/Attachment/:AttachmentID
```
> **Why GET passes `SessionID` as query?**
> Znuny’s REST GET handlers do not parse JSON request bodies. Always pass `?SessionID=...` for **GET** routes.
---
## 2) CustomerUser – cURL Examples
### Create
```bash
curl -k -sS -X POST \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/CustomerUser' \
-H 'Content-Type: application/json' \
-d '{
"UserLogin": "jane.doe",
"UserEmail": "jane.doe@example.com",
"UserFirstname": "Jane",
"UserLastname": "Doe",
"UserCustomerID": "Servernah",
"UserPassword": "S3cr3t!2025",
"ValidID": 1
}'
# => {"UserLogin":"jane.doe"}
```
### Get
```bash
curl -k -sS -X POST \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/CustomerUser/Get' \
-H 'Content-Type: application/json' \
-d '{"UserLogin":"jane.doe"}'
```
### Update
```bash
curl -k -sS -X POST \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/CustomerUser/Update' \
-H 'Content-Type: application/json' \
-d '{
"UserLogin": "jane.doe",
"UserEmail": "jane+1@example.com",
"UserFirstname": "Janet",
"UserLastname": "Doe",
"UserCustomerID": "Servernah",
"ValidID": 1
}'
# => {"UserLogin":"jane.doe"}
```
### Search (by email)
```bash
curl -k -sS -X POST \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/CustomerUser/Search' \
-H 'Content-Type: application/json' \
-d '{"PostMasterSearch":"jane.doe@example.com"}'
# => {"CustomerUsers":["jane.doe","\"Jane Doe\" <jane.doe@example.com>"]}
```
---
## 3) Session – cURL Examples
### Create Session
```bash
curl -k -sS -X POST \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/Session' \
-H 'Content-Type: application/json' \
-d '{"UserLogin":"Toney.Webala","Password":"<PASSWORD>"}'
# => {"SessionID":"<token>"}
```
### Get Session
```bash
curl -k -sS -G \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/Session/<SessionID>' \
-H 'Accept: application/json'
```
### Delete Session
```bash
curl -k -sS -X DELETE \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/Session/<SessionID>'
```
---
## 4) Ticket – cURL Examples
### Create (with first article & attachments)
```bash
# Prepare base64 file content
FILE64="$(openssl base64 -A -in ./screenshot.png)"
curl -k -sS -X POST \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/Ticket' \
-H 'Content-Type: application/json' \
-d "{
\"SessionID\": \"<SessionID>\",
\"Ticket\": {
\"Title\": \"Portal: cannot access service catalog\",
\"Queue\": \"Raw\",
\"State\": \"new\",
\"Priority\": \"3 normal\",
\"CustomerUser\": \"jane.doe\"
},
\"Article\": {
\"Subject\": \"Login issue\",
\"Body\": \"User reports SSO success, but portal shows empty page.\",
\"ContentType\": \"text/plain; charset=utf-8\",
\"Attachment\": [
{
\"Filename\": \"screenshot.png\",
\"ContentType\": \"image/png\",
\"Content\": \"${FILE64}\"
}
]
}
}"
# => {"ArticleID":123,"TicketID":"99","TicketNumber":"2025..."}
```
### Get (note the `-G` & query param)
```bash
curl -k -sS -G \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/Ticket/99' \
--data-urlencode 'SessionID=<SessionID>' \
-H 'Accept: application/json'
```
### Update (change priority/state and add a note with attachment)
```bash
NOTE64="$(printf 'Investigating; appears VPN-related' | openssl base64 -A)"
curl -k -sS -X PATCH \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/Ticket/99' \
-H 'Content-Type: application/json' \
-d "{
\"SessionID\": \"<SessionID>\",
\"Ticket\": {
\"State\": \"open\",
\"Priority\": \"4 high\"
},
\"Article\": {
\"Subject\": \"Follow-up\",
\"Body\": \"Customer confirmed the issue is VPN-only.\",
\"ContentType\": \"text/plain; charset=utf-8\",
\"Attachment\": [
{
\"Filename\": \"note.txt\",
\"ContentType\": \"text/plain\",
\"Content\": \"${NOTE64}\"
}
]
}
}"
# => {"ArticleID":..., "TicketID":"99", "TicketNumber":"..."}
```
### Search
```bash
# ARRAY of TicketIDs for a customer & queue set
curl -k -sS -X POST \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/Ticket/Search' \
-H 'Content-Type: application/json' \
-d '{
"SessionID": "<SessionID>",
"CustomerUserLogin": "jane.doe",
"Queues": ["Raw"],
"StateType": ["new", "open", "pending reminder", "pending auto"],
"OrderBy": ["Created"],
"SortBy": "Down",
"Limit": 50,
"Result": "ARRAY"
}'
# => {"TicketID":["19","18","17"]}
# HASH (full details) – keep volume in mind
curl -k -sS -X POST \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/Ticket/Search' \
-H 'Content-Type: application/json' \
-d '{
"SessionID": "<SessionID>",
"Queues": ["Raw"],
"States": ["new","open"],
"OrderBy": ["Created"],
"SortBy": "Down",
"Limit": 20,
"Result": "HASH"
}'
```
### History
```bash
curl -k -sS -G \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/Ticket/History/99' \
--data-urlencode 'SessionID=<SessionID>' \
-H 'Accept: application/json'
```
### Attachment Download
```bash
# Returns JSON with base64 content
curl -k -sS -G \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/Ticket/Article/<ArticleID>/Attachment/<AttachmentID>' \
--data-urlencode 'SessionID=<SessionID>' \
-H 'Accept: application/json'
# Save the file locally (requires jq)
curl -k -sS -G \
'https://<host>/otrs/nph-genericinterface.pl/Webservice/znuny_portal/Ticket/Article/<ArticleID>/Attachment/<AttachmentID>' \
--data-urlencode 'SessionID=<SessionID>' \
-H 'Accept: application/json' \
| jq -r '.Content' | base64 --decode > downloaded.bin
```
---
## 5) REST Sequence Diagrams
> **Note:** These diagrams assume the webservice name `znuny_portal` and show session auth + common flows.
### 5.1 Session + Ticket Create
```mermaid
sequenceDiagram
autonumber
participant C as Client
participant Z as Znuny Provider (znuny_portal)
C->>Z: POST /Session {UserLogin, Password}
Z-->>C: {SessionID}
C->>Z: POST /Ticket {SessionID, Ticket{...}, Article{...,[Attachment]}}
Z-->>C: {TicketID, TicketNumber, ArticleID}
```
### 5.2 Ticket Get (GET with query param)
```mermaid
sequenceDiagram
autonumber
participant C as Client
participant Z as Znuny Provider
C->>Z: GET /Ticket/:TicketID?SessionID=...
Z-->>C: {Ticket:[{...}]}
```
### 5.3 Ticket Update + Note + Attachment
```mermaid
sequenceDiagram
autonumber
participant C as Client
participant Z as Znuny Provider
C->>Z: PATCH /Ticket/:TicketID {SessionID, Ticket{State,Priority}, Article{Subject,Body,[Attachment]}}
Z-->>C: {ArticleID, TicketID, TicketNumber}
```
### 5.4 Ticket Search
```mermaid
sequenceDiagram
autonumber
participant C as Client
participant Z as Znuny Provider
C->>Z: POST /Ticket/Search {SessionID, filters... Result: ARRAY|HASH}
Z-->>C: {TicketID:[...]} or {Ticket:[...]}
```
### 5.5 Ticket History
```mermaid
sequenceDiagram
autonumber
participant C as Client
participant Z as Znuny Provider
C->>Z: GET /Ticket/History/:TicketID?SessionID=...
Z-->>C: {TicketHistory:[{History:[...]...}]}
```
### 5.6 Attachment Download
```mermaid
sequenceDiagram
autonumber
participant C as Client
participant Z as Znuny Provider
C->>Z: GET /Ticket/Article/:ArticleID/Attachment/:AttachmentID?SessionID=...
Z-->>C: {Filename, ContentType, Content(base64)}
```
---
## 6) Request/Response Parameters (at a glance)
| Operation | Method & Route | Auth | Request Body (key fields) | Response (key fields) |
|---|---|---|---|---|
| **SessionCreate** | `POST /Session` | none | `UserLogin, Password` | `SessionID` |
| **SessionGet** | `GET /Session/:SessionID` | query | *(none)* | Session info |
| **SessionDelete** | `DELETE /Session/:SessionID` | query | *(none)* | *(empty)* |
| **CustomerUserCreate** | `POST /CustomerUser` | body | `UserLogin, UserEmail, UserFirstname, UserLastname, UserCustomerID, [UserPassword], ValidID` | `UserLogin` |
| **CustomerUserGet** | `POST /CustomerUser/Get` | body | `UserLogin` | Full user record |
| **CustomerUserUpdate** | `POST /CustomerUser/Update` | body | `UserLogin, [fields…]` | `UserLogin` |
| **CustomerUserSearch** | `POST /CustomerUser/Search` | body | `PostMasterSearch` (email) or other filters | `CustomerUsers: [Login, "Full Name <email>"]` |
| **TicketCreate** | `POST /Ticket` | body | `SessionID, Ticket{Title,Queue,State,Priority,CustomerUser}, Article{Subject,Body,ContentType,[Attachment[]]}` | `TicketID, TicketNumber, ArticleID` |
| **TicketGet** | `GET /Ticket/:TicketID` | query | `?SessionID=...` | `Ticket:[{...}]` |
| **TicketUpdate** | `PATCH /Ticket/:TicketID` | body | `SessionID, Ticket{State,Priority,...}, [Article{...,[Attachment[]]}]` | `ArticleID, TicketID, TicketNumber` |
| **TicketSearch** | `POST /Ticket/Search` | body | `SessionID, filters..., Result=ARRAY|HASH` | `TicketID:[...]` or `Ticket:[...]` |
| **TicketHistoryGet** | `GET /Ticket/History/:TicketID` | query | `?SessionID=...` | `TicketHistory:[{History:[...]...}]` |
| **ArticleAttachmentGet** | `GET /Ticket/Article/:ArticleID/Attachment/:AttachmentID` | query | `?SessionID=...` | `{Filename, ContentType, Content(base64)}` |
**Notes**
- For **GET** routes, pass `SessionID` as a **query** parameter.
- Attachment **upload** only via `Article.Attachment[]` when creating/updating tickets.
- Attachment **download** uses the dedicated `ArticleAttachmentGet` route.
- Email validations and customer company must be valid (`CustomerUserEmailUniqCheck`, `CustomerCompanyValid`).
---
## 7) Common Errors
| ErrorCode | Meaning | Typical Fix |
|---|---|---|
| `*.AuthFail` | User/session not authenticated or session missing on GET | Create session; pass `?SessionID=...` on GET. |
| `*.MissingParameter` | Required field absent | Add required JSON field(s). |
| `CustomerUserCreate.EmailInvalid` | Email blocked by config | Use an allowed domain or relax validation. |
| `TicketCreate.InvalidParameter` | Wrong field shape (e.g., DynamicField format) | Follow documented JSON structure. |
---
## 8) Operational Tips
- After editing YAML or Custom modules:
```bash
/opt/otrs/bin/otrs.Console.pl Maint::Config::Rebuild
/opt/otrs/bin/otrs.Console.pl Maint::Cache::Delete
sudo systemctl reload apache2
```
- Use `Dev::Tools::GenericInterface::DebugRead` to trace requests:
```bash
/opt/otrs/bin/otrs.Console.pl Dev::Tools::GenericInterface::DebugRead \
--webservice-id <ID> --with-data \
--created-at-or-after "$(date -u --date='5 minutes ago' '+%Y-%m-%d %H:%M:%S')" \
--limit 20
```
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment