[{"data":1,"prerenderedAt":1208},["ShallowReactive",2],{"blog-post-python-automation-ticketsysteme-rest-api":3},{"id":4,"title":5,"body":6,"date":1198,"description":1199,"draft":1200,"extension":1201,"meta":1202,"navigation":145,"path":1204,"seo":1205,"stem":1206,"__hash__":1207},"blog\u002Fblog\u002Fpython-automation-ticketsysteme-rest-api.md","Ticket-System-Automation mit Python: REST-APIs, Libraries und praktische Patterns",{"type":7,"value":8,"toc":1177},"minimark",[9,13,30,33,38,105,107,111,116,123,271,277,283,288,291,402,407,427,429,433,440,444,604,608,611,629,633,649,651,655,659,734,738,804,808,857,859,863,866,963,965,969,972,1036,1038,1042,1125,1127,1131,1137,1157,1165,1167,1173],[10,11,5],"h1",{"id":12},"ticket-system-automation-mit-python-rest-apis-libraries-und-praktische-patterns",[14,15,16,17,21,22,25,26,29],"p",{},"Python ist die Lingua Franca der Systemautomation – und bei Open-Source-Ticket-Systemen wie ",[18,19,20],"strong",{},"Zammad",", ",[18,23,24],{},"OTOBO"," und ",[18,27,28],{},"Znuny"," (OTRS-Fork) bietet sich eine breite Palette an Integrationsoptionen. Dieser Artikel ordnet die verfügbaren Python-Clients, offiziellen APIs und bewährte Patterns für wiederkehrende Automatisierungsaufgaben ein.",[31,32],"hr",{},[34,35,37],"h2",{"id":36},"die-drei-wege-zur-python-integration","Die drei Wege zur Python-Integration",[39,40,41,57],"table",{},[42,43,44],"thead",{},[45,46,47,51,54],"tr",{},[48,49,50],"th",{},"Ansatz",[48,52,53],{},"Wann sinnvoll",[48,55,56],{},"Komplexität",[58,59,60,74,91],"tbody",{},[45,61,62,68,71],{},[63,64,65],"td",{},[18,66,67],{},"Offizielle Python-Client-Library",[63,69,70],{},"Schneller Einstieg, dokumentierte Endpoints",[63,72,73],{},"Niedrig",[45,75,76,85,88],{},[63,77,78],{},[18,79,80,81],{},"Direct REST-API mit ",[82,83,84],"code",{},"requests",[63,86,87],{},"Volle Kontrolle, Custom-Endpoints, Debugging",[63,89,90],{},"Mittel",[45,92,93,99,102],{},[63,94,95,98],{},[18,96,97],{},"Wrapper\u002FETL-Frameworks"," (z. B. open-ticket-ai)",[63,100,101],{},"Multi-System-Automation, Pipelines, RAG",[63,103,104],{},"Hoch",[31,106],{},[34,108,110],{"id":109},"zammad-zammad-py-und-die-rest-api","Zammad: Zammad-Py und die REST-API",[112,113,115],"h3",{"id":114},"zammad-py-community-client","Zammad-Py (Community-Client)",[14,117,118,119,122],{},"Die Library ",[82,120,121],{},"zammad_py"," bietet eine objektorientierte Schicht über die Zammad REST-API:",[124,125,130],"pre",{"className":126,"code":127,"language":128,"meta":129,"style":129},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","from zammad_py import ZammadAPI\n\nclient = ZammadAPI(\n    url=\"https:\u002F\u002Fhelpdesk.example.com\u002Fapi\u002Fv1\",\n    http_token=\"your_api_token\"\n)\n\n# Tickets abrufen\nfor ticket in client.ticket.all():\n    print(f\"{ticket['number']}: {ticket['title']}\")\n\n# Ticket erstellen\nticket = client.ticket.create({\n    \"title\": \"Server-Alert: CPU-Load\",\n    \"group\": \"IT-Operations\",\n    \"customer\": \"monitoring@example.com\",\n    \"article\": {\n        \"subject\": \"Alert\",\n        \"body\": \"CPU usage exceeded 90% on server-01\",\n        \"type\": \"note\",\n        \"internal\": False\n    }\n})\n","python","",[82,131,132,140,147,153,159,165,171,176,182,188,194,199,205,211,217,223,229,235,241,247,253,259,265],{"__ignoreMap":129},[133,134,137],"span",{"class":135,"line":136},"line",1,[133,138,139],{},"from zammad_py import ZammadAPI\n",[133,141,143],{"class":135,"line":142},2,[133,144,146],{"emptyLinePlaceholder":145},true,"\n",[133,148,150],{"class":135,"line":149},3,[133,151,152],{},"client = ZammadAPI(\n",[133,154,156],{"class":135,"line":155},4,[133,157,158],{},"    url=\"https:\u002F\u002Fhelpdesk.example.com\u002Fapi\u002Fv1\",\n",[133,160,162],{"class":135,"line":161},5,[133,163,164],{},"    http_token=\"your_api_token\"\n",[133,166,168],{"class":135,"line":167},6,[133,169,170],{},")\n",[133,172,174],{"class":135,"line":173},7,[133,175,146],{"emptyLinePlaceholder":145},[133,177,179],{"class":135,"line":178},8,[133,180,181],{},"# Tickets abrufen\n",[133,183,185],{"class":135,"line":184},9,[133,186,187],{},"for ticket in client.ticket.all():\n",[133,189,191],{"class":135,"line":190},10,[133,192,193],{},"    print(f\"{ticket['number']}: {ticket['title']}\")\n",[133,195,197],{"class":135,"line":196},11,[133,198,146],{"emptyLinePlaceholder":145},[133,200,202],{"class":135,"line":201},12,[133,203,204],{},"# Ticket erstellen\n",[133,206,208],{"class":135,"line":207},13,[133,209,210],{},"ticket = client.ticket.create({\n",[133,212,214],{"class":135,"line":213},14,[133,215,216],{},"    \"title\": \"Server-Alert: CPU-Load\",\n",[133,218,220],{"class":135,"line":219},15,[133,221,222],{},"    \"group\": \"IT-Operations\",\n",[133,224,226],{"class":135,"line":225},16,[133,227,228],{},"    \"customer\": \"monitoring@example.com\",\n",[133,230,232],{"class":135,"line":231},17,[133,233,234],{},"    \"article\": {\n",[133,236,238],{"class":135,"line":237},18,[133,239,240],{},"        \"subject\": \"Alert\",\n",[133,242,244],{"class":135,"line":243},19,[133,245,246],{},"        \"body\": \"CPU usage exceeded 90% on server-01\",\n",[133,248,250],{"class":135,"line":249},20,[133,251,252],{},"        \"type\": \"note\",\n",[133,254,256],{"class":135,"line":255},21,[133,257,258],{},"        \"internal\": False\n",[133,260,262],{"class":135,"line":261},22,[133,263,264],{},"    }\n",[133,266,268],{"class":135,"line":267},23,[133,269,270],{},"})\n",[14,272,273,276],{},[18,274,275],{},"Vorteile:"," Typische CRUD-Operationen abstrahiert, Fehlerhandling integriert, aktive Community.",[14,278,279,282],{},[18,280,281],{},"Limitationen:"," Nicht alle API-Features (z. B. Core Workflows, Object-Manager-Changes) sind gemappt.",[112,284,80,286],{"id":285},"direct-rest-api-mit-requests",[82,287,84],{},[14,289,290],{},"Für fortgeschrittene Szenarien (Webhooks, Tags, Time Accounting) empfiehlt sich direkter API-Zugriff:",[124,292,294],{"className":126,"code":293,"language":128,"meta":129,"style":129},"import requests\n\nheaders = {\n    \"Authorization\": \"Bearer your_token\",\n    \"Content-Type\": \"application\u002Fjson\"\n}\n\n# Suche mit Query\nresponse = requests.get(\n    \"https:\u002F\u002Fhelpdesk.example.com\u002Fapi\u002Fv1\u002Ftickets\u002Fsearch\",\n    headers=headers,\n    params={\"query\": \"state:new AND priority:3\", \"limit\": 50}\n)\n\n# Bulk-Update mehrerer Tickets\ntickets = response.json()[\"assets\"][\"Ticket\"]\nfor ticket_id in tickets:\n    requests.put(\n        f\"https:\u002F\u002Fhelpdesk.example.com\u002Fapi\u002Fv1\u002Ftickets\u002F{ticket_id}\",\n        headers=headers,\n        json={\"state\": \"open\", \"owner\": \"admin@example.com\"}\n    )\n",[82,295,296,301,305,310,315,320,325,329,334,339,344,349,354,358,362,367,372,377,382,387,392,397],{"__ignoreMap":129},[133,297,298],{"class":135,"line":136},[133,299,300],{},"import requests\n",[133,302,303],{"class":135,"line":142},[133,304,146],{"emptyLinePlaceholder":145},[133,306,307],{"class":135,"line":149},[133,308,309],{},"headers = {\n",[133,311,312],{"class":135,"line":155},[133,313,314],{},"    \"Authorization\": \"Bearer your_token\",\n",[133,316,317],{"class":135,"line":161},[133,318,319],{},"    \"Content-Type\": \"application\u002Fjson\"\n",[133,321,322],{"class":135,"line":167},[133,323,324],{},"}\n",[133,326,327],{"class":135,"line":173},[133,328,146],{"emptyLinePlaceholder":145},[133,330,331],{"class":135,"line":178},[133,332,333],{},"# Suche mit Query\n",[133,335,336],{"class":135,"line":184},[133,337,338],{},"response = requests.get(\n",[133,340,341],{"class":135,"line":190},[133,342,343],{},"    \"https:\u002F\u002Fhelpdesk.example.com\u002Fapi\u002Fv1\u002Ftickets\u002Fsearch\",\n",[133,345,346],{"class":135,"line":196},[133,347,348],{},"    headers=headers,\n",[133,350,351],{"class":135,"line":201},[133,352,353],{},"    params={\"query\": \"state:new AND priority:3\", \"limit\": 50}\n",[133,355,356],{"class":135,"line":207},[133,357,170],{},[133,359,360],{"class":135,"line":213},[133,361,146],{"emptyLinePlaceholder":145},[133,363,364],{"class":135,"line":219},[133,365,366],{},"# Bulk-Update mehrerer Tickets\n",[133,368,369],{"class":135,"line":225},[133,370,371],{},"tickets = response.json()[\"assets\"][\"Ticket\"]\n",[133,373,374],{"class":135,"line":231},[133,375,376],{},"for ticket_id in tickets:\n",[133,378,379],{"class":135,"line":237},[133,380,381],{},"    requests.put(\n",[133,383,384],{"class":135,"line":243},[133,385,386],{},"        f\"https:\u002F\u002Fhelpdesk.example.com\u002Fapi\u002Fv1\u002Ftickets\u002F{ticket_id}\",\n",[133,388,389],{"class":135,"line":249},[133,390,391],{},"        headers=headers,\n",[133,393,394],{"class":135,"line":255},[133,395,396],{},"        json={\"state\": \"open\", \"owner\": \"admin@example.com\"}\n",[133,398,399],{"class":135,"line":261},[133,400,401],{},"    )\n",[14,403,404],{},[18,405,406],{},"Ressourcen:",[408,409,410,420],"ul",{},[411,412,413],"li",{},[414,415,419],"a",{"href":416,"rel":417},"https:\u002F\u002Fpypi.org\u002Fproject\u002Fzammad-py\u002F",[418],"nofollow","zammad_py auf PyPI",[411,421,422],{},[414,423,426],{"href":424,"rel":425},"https:\u002F\u002Fdocs.zammad.com\u002Fen\u002Flatest\u002Fapi\u002Fintro.html",[418],"Zammad REST-API Docs",[31,428],{},[34,430,432],{"id":431},"otobo-znuny-die-opm-schnittstelle-und-genericinterface","OTOBO & Znuny: Die OPM-Schnittstelle und GenericInterface",[14,434,435,436,439],{},"OTOBO und Znuny (als OTRS-Forks) nutzen eine ähnliche Architektur: Das ",[18,437,438],{},"GenericInterface"," bietet REST\u002FSOAP-Webservices, die per Python ansprechbar sind.",[112,441,443],{"id":442},"pattern-session-basierte-authentifizierung","Pattern: Session-basierte Authentifizierung",[124,445,447],{"className":126,"code":446,"language":128,"meta":129,"style":129},"import requests\n\n# 1. SessionToken erhalten\nsession_resp = requests.post(\n    \"https:\u002F\u002Fotobo.example.com\u002Fotobo\u002Frpc.pl\",\n    json={\n        \"UserLogin\": \"api_user\",\n        \"Password\": \"api_pass\"\n    }\n)\nsession_token = session_resp.json()[\"SessionID\"]\n\n# 2. Ticket erstellen\nticket_create = requests.post(\n    \"https:\u002F\u002Fotobo.example.com\u002Fotobo\u002Fnph-genericinterface.pl\u002FWebservice\u002FTicketConnectorREST\u002FTicket\",\n    headers={\"Authorization\": f\"Token {session_token}\"},\n    json={\n        \"Ticket\": {\n            \"Title\": \"API-Generated Alert\",\n            \"Queue\": \"IT-Support\",\n            \"State\": \"new\",\n            \"Priority\": \"3 normal\",\n            \"CustomerUser\": \"monitoring\"\n        },\n        \"Article\": {\n            \"Subject\": \"Automated Alert\",\n            \"Body\": \"Alert details...\",\n            \"ContentType\": \"text\u002Fplain; charset=utf8\"\n        }\n    }\n)\n",[82,448,449,453,457,462,467,472,477,482,487,491,495,500,504,509,514,519,524,528,533,538,543,548,553,558,564,570,576,582,588,594,599],{"__ignoreMap":129},[133,450,451],{"class":135,"line":136},[133,452,300],{},[133,454,455],{"class":135,"line":142},[133,456,146],{"emptyLinePlaceholder":145},[133,458,459],{"class":135,"line":149},[133,460,461],{},"# 1. SessionToken erhalten\n",[133,463,464],{"class":135,"line":155},[133,465,466],{},"session_resp = requests.post(\n",[133,468,469],{"class":135,"line":161},[133,470,471],{},"    \"https:\u002F\u002Fotobo.example.com\u002Fotobo\u002Frpc.pl\",\n",[133,473,474],{"class":135,"line":167},[133,475,476],{},"    json={\n",[133,478,479],{"class":135,"line":173},[133,480,481],{},"        \"UserLogin\": \"api_user\",\n",[133,483,484],{"class":135,"line":178},[133,485,486],{},"        \"Password\": \"api_pass\"\n",[133,488,489],{"class":135,"line":184},[133,490,264],{},[133,492,493],{"class":135,"line":190},[133,494,170],{},[133,496,497],{"class":135,"line":196},[133,498,499],{},"session_token = session_resp.json()[\"SessionID\"]\n",[133,501,502],{"class":135,"line":201},[133,503,146],{"emptyLinePlaceholder":145},[133,505,506],{"class":135,"line":207},[133,507,508],{},"# 2. Ticket erstellen\n",[133,510,511],{"class":135,"line":213},[133,512,513],{},"ticket_create = requests.post(\n",[133,515,516],{"class":135,"line":219},[133,517,518],{},"    \"https:\u002F\u002Fotobo.example.com\u002Fotobo\u002Fnph-genericinterface.pl\u002FWebservice\u002FTicketConnectorREST\u002FTicket\",\n",[133,520,521],{"class":135,"line":225},[133,522,523],{},"    headers={\"Authorization\": f\"Token {session_token}\"},\n",[133,525,526],{"class":135,"line":231},[133,527,476],{},[133,529,530],{"class":135,"line":237},[133,531,532],{},"        \"Ticket\": {\n",[133,534,535],{"class":135,"line":243},[133,536,537],{},"            \"Title\": \"API-Generated Alert\",\n",[133,539,540],{"class":135,"line":249},[133,541,542],{},"            \"Queue\": \"IT-Support\",\n",[133,544,545],{"class":135,"line":255},[133,546,547],{},"            \"State\": \"new\",\n",[133,549,550],{"class":135,"line":261},[133,551,552],{},"            \"Priority\": \"3 normal\",\n",[133,554,555],{"class":135,"line":267},[133,556,557],{},"            \"CustomerUser\": \"monitoring\"\n",[133,559,561],{"class":135,"line":560},24,[133,562,563],{},"        },\n",[133,565,567],{"class":135,"line":566},25,[133,568,569],{},"        \"Article\": {\n",[133,571,573],{"class":135,"line":572},26,[133,574,575],{},"            \"Subject\": \"Automated Alert\",\n",[133,577,579],{"class":135,"line":578},27,[133,580,581],{},"            \"Body\": \"Alert details...\",\n",[133,583,585],{"class":135,"line":584},28,[133,586,587],{},"            \"ContentType\": \"text\u002Fplain; charset=utf8\"\n",[133,589,591],{"class":135,"line":590},29,[133,592,593],{},"        }\n",[133,595,597],{"class":135,"line":596},30,[133,598,264],{},[133,600,602],{"class":135,"line":601},31,[133,603,170],{},[112,605,607],{"id":606},"python-wrapper-für-otoboznuny","Python-Wrapper für OTOBO\u002FZnuny",[14,609,610],{},"Im Vergleich zu Zammad gibt es weniger reife Python-Clients. Empfohlen wird:",[612,613,614,623],"ol",{},[411,615,616,619,620,622],{},[18,617,618],{},"Eigener Thin-Client:"," 100-200 Zeilen Wrapper um ",[82,621,84],{}," für projektspezifische Endpoints",[411,624,625,628],{},[18,626,627],{},"Open Ticket AI:"," Framework mit unified Ticket-Modell für OTOBO, Znuny und Zammad",[14,630,631],{},[18,632,406],{},[408,634,635,642],{},[411,636,637],{},[414,638,641],{"href":639,"rel":640},"https:\u002F\u002Fdoc.otobo.org\u002Fmanual\u002Fdeveloper\u002Fstable\u002Fen\u002Fcontent\u002Fwebservices.html",[418],"OTOBO GenericInterface Docs",[411,643,644],{},[414,645,648],{"href":646,"rel":647},"https:\u002F\u002Fdoc.znuny.org\u002Fmanual\u002Fdeveloper\u002F7.0\u002Fen\u002Fhtml\u002Fwebservices.html",[418],"Znuny Developer Docs",[31,650],{},[34,652,654],{"id":653},"praktische-automation-patterns","Praktische Automation-Patterns",[112,656,658],{"id":657},"pattern-1-monitoring-ticket-inbound","Pattern 1: Monitoring → Ticket (Inbound)",[124,660,662],{"className":126,"code":661,"language":128,"meta":129,"style":129},"def create_ticket_from_alert(alert_data: dict, system: str):\n    \"\"\"Unified interface für Zammad\u002FOTOBO\u002FZnuny\"\"\"\n    \n    if system == \"zammad\":\n        return zammad_client.ticket.create({\n            \"title\": alert_data[\"summary\"],\n            \"group\": \"Monitoring\",\n            \"article\": {\"body\": alert_data[\"details\"]}\n        })\n    elif system in [\"otobo\", \"znuny\"]:\n        return otobo_session.post(\n            f\"\u002FTicketConnectorREST\u002FTicket\",\n            json=map_alert_to_otobo(alert_data)\n        )\n",[82,663,664,669,674,679,684,689,694,699,704,709,714,719,724,729],{"__ignoreMap":129},[133,665,666],{"class":135,"line":136},[133,667,668],{},"def create_ticket_from_alert(alert_data: dict, system: str):\n",[133,670,671],{"class":135,"line":142},[133,672,673],{},"    \"\"\"Unified interface für Zammad\u002FOTOBO\u002FZnuny\"\"\"\n",[133,675,676],{"class":135,"line":149},[133,677,678],{},"    \n",[133,680,681],{"class":135,"line":155},[133,682,683],{},"    if system == \"zammad\":\n",[133,685,686],{"class":135,"line":161},[133,687,688],{},"        return zammad_client.ticket.create({\n",[133,690,691],{"class":135,"line":167},[133,692,693],{},"            \"title\": alert_data[\"summary\"],\n",[133,695,696],{"class":135,"line":173},[133,697,698],{},"            \"group\": \"Monitoring\",\n",[133,700,701],{"class":135,"line":178},[133,702,703],{},"            \"article\": {\"body\": alert_data[\"details\"]}\n",[133,705,706],{"class":135,"line":184},[133,707,708],{},"        })\n",[133,710,711],{"class":135,"line":190},[133,712,713],{},"    elif system in [\"otobo\", \"znuny\"]:\n",[133,715,716],{"class":135,"line":196},[133,717,718],{},"        return otobo_session.post(\n",[133,720,721],{"class":135,"line":201},[133,722,723],{},"            f\"\u002FTicketConnectorREST\u002FTicket\",\n",[133,725,726],{"class":135,"line":207},[133,727,728],{},"            json=map_alert_to_otobo(alert_data)\n",[133,730,731],{"class":135,"line":213},[133,732,733],{},"        )\n",[112,735,737],{"id":736},"pattern-2-ticket-eskalation-outbound","Pattern 2: Ticket-Eskalation (Outbound)",[124,739,741],{"className":126,"code":740,"language":128,"meta":129,"style":129},"def escalate_stale_tickets(system_client, hours_threshold: int = 24):\n    \"\"\"Findet und eskaliert Tickets ohne Agenten-Aktivität\"\"\"\n    \n    stale_tickets = system_client.search(\n        query=f\"state:open AND last_contact_agent:[* TO now-{hours_threshold}h]\"\n    )\n    \n    for ticket in stale_tickets:\n        system_client.update(ticket[\"id\"], {\n            \"priority\": \"high\",\n            \"tags\": [\"auto-escalated\"],\n            \"note\": f\"Automatisch eskaliert nach {hours_threshold}h Inaktivität\"\n        })\n",[82,742,743,748,753,757,762,767,771,775,780,785,790,795,800],{"__ignoreMap":129},[133,744,745],{"class":135,"line":136},[133,746,747],{},"def escalate_stale_tickets(system_client, hours_threshold: int = 24):\n",[133,749,750],{"class":135,"line":142},[133,751,752],{},"    \"\"\"Findet und eskaliert Tickets ohne Agenten-Aktivität\"\"\"\n",[133,754,755],{"class":135,"line":149},[133,756,678],{},[133,758,759],{"class":135,"line":155},[133,760,761],{},"    stale_tickets = system_client.search(\n",[133,763,764],{"class":135,"line":161},[133,765,766],{},"        query=f\"state:open AND last_contact_agent:[* TO now-{hours_threshold}h]\"\n",[133,768,769],{"class":135,"line":167},[133,770,401],{},[133,772,773],{"class":135,"line":173},[133,774,678],{},[133,776,777],{"class":135,"line":178},[133,778,779],{},"    for ticket in stale_tickets:\n",[133,781,782],{"class":135,"line":184},[133,783,784],{},"        system_client.update(ticket[\"id\"], {\n",[133,786,787],{"class":135,"line":190},[133,788,789],{},"            \"priority\": \"high\",\n",[133,791,792],{"class":135,"line":196},[133,793,794],{},"            \"tags\": [\"auto-escalated\"],\n",[133,796,797],{"class":135,"line":201},[133,798,799],{},"            \"note\": f\"Automatisch eskaliert nach {hours_threshold}h Inaktivität\"\n",[133,801,802],{"class":135,"line":207},[133,803,708],{},[112,805,807],{"id":806},"pattern-3-datenmigration-cross-system","Pattern 3: Datenmigration (Cross-System)",[124,809,811],{"className":126,"code":810,"language":128,"meta":129,"style":129},"# Zammad → OTOBO Migration\nfor ticket in zammad_client.ticket.all():\n    mapped_ticket = {\n        \"Title\": ticket[\"title\"],\n        \"Queue\": map_queue(ticket[\"group\"]),\n        \"State\": map_state(ticket[\"state\"]),\n        \"CustomerUser\": ticket[\"customer\"]\n    }\n    otobo_client.ticket.create(mapped_ticket)\n",[82,812,813,818,823,828,833,838,843,848,852],{"__ignoreMap":129},[133,814,815],{"class":135,"line":136},[133,816,817],{},"# Zammad → OTOBO Migration\n",[133,819,820],{"class":135,"line":142},[133,821,822],{},"for ticket in zammad_client.ticket.all():\n",[133,824,825],{"class":135,"line":149},[133,826,827],{},"    mapped_ticket = {\n",[133,829,830],{"class":135,"line":155},[133,831,832],{},"        \"Title\": ticket[\"title\"],\n",[133,834,835],{"class":135,"line":161},[133,836,837],{},"        \"Queue\": map_queue(ticket[\"group\"]),\n",[133,839,840],{"class":135,"line":167},[133,841,842],{},"        \"State\": map_state(ticket[\"state\"]),\n",[133,844,845],{"class":135,"line":173},[133,846,847],{},"        \"CustomerUser\": ticket[\"customer\"]\n",[133,849,850],{"class":135,"line":178},[133,851,264],{},[133,853,854],{"class":135,"line":184},[133,855,856],{},"    otobo_client.ticket.create(mapped_ticket)\n",[31,858],{},[34,860,862],{"id":861},"error-handling-und-resilience","Error Handling und Resilience",[14,864,865],{},"Produktive Automation erfordert robustes Error Handling:",[124,867,869],{"className":126,"code":868,"language":128,"meta":129,"style":129},"from tenacity import retry, stop_after_attempt, wait_exponential\nimport logging\n\n@retry(\n    stop=stop_after_attempt(3),\n    wait=wait_exponential(multiplier=1, min=4, max=10)\n)\ndef safe_api_call(func, *args, **kwargs):\n    try:\n        return func(*args, **kwargs)\n    except requests.exceptions.HTTPError as e:\n        if e.response.status_code == 429:  # Rate Limit\n            raise  # Trigger retry\n        elif e.response.status_code == 401:\n            logging.error(\"Authentication failed - check API token\")\n            raise\n        else:\n            logging.error(f\"API Error: {e}\")\n            raise\n",[82,870,871,876,881,885,890,895,900,904,909,914,919,924,929,934,939,944,949,954,959],{"__ignoreMap":129},[133,872,873],{"class":135,"line":136},[133,874,875],{},"from tenacity import retry, stop_after_attempt, wait_exponential\n",[133,877,878],{"class":135,"line":142},[133,879,880],{},"import logging\n",[133,882,883],{"class":135,"line":149},[133,884,146],{"emptyLinePlaceholder":145},[133,886,887],{"class":135,"line":155},[133,888,889],{},"@retry(\n",[133,891,892],{"class":135,"line":161},[133,893,894],{},"    stop=stop_after_attempt(3),\n",[133,896,897],{"class":135,"line":167},[133,898,899],{},"    wait=wait_exponential(multiplier=1, min=4, max=10)\n",[133,901,902],{"class":135,"line":173},[133,903,170],{},[133,905,906],{"class":135,"line":178},[133,907,908],{},"def safe_api_call(func, *args, **kwargs):\n",[133,910,911],{"class":135,"line":184},[133,912,913],{},"    try:\n",[133,915,916],{"class":135,"line":190},[133,917,918],{},"        return func(*args, **kwargs)\n",[133,920,921],{"class":135,"line":196},[133,922,923],{},"    except requests.exceptions.HTTPError as e:\n",[133,925,926],{"class":135,"line":201},[133,927,928],{},"        if e.response.status_code == 429:  # Rate Limit\n",[133,930,931],{"class":135,"line":207},[133,932,933],{},"            raise  # Trigger retry\n",[133,935,936],{"class":135,"line":213},[133,937,938],{},"        elif e.response.status_code == 401:\n",[133,940,941],{"class":135,"line":219},[133,942,943],{},"            logging.error(\"Authentication failed - check API token\")\n",[133,945,946],{"class":135,"line":225},[133,947,948],{},"            raise\n",[133,950,951],{"class":135,"line":231},[133,952,953],{},"        else:\n",[133,955,956],{"class":135,"line":237},[133,957,958],{},"            logging.error(f\"API Error: {e}\")\n",[133,960,961],{"class":135,"line":243},[133,962,948],{},[31,964],{},[34,966,968],{"id":967},"dsgvo-aspekte-bei-api-automation","DSGVO-Aspekte bei API-Automation",[14,970,971],{},"Beim automatisierten Zugriff auf Ticket-Daten beachten:",[39,973,974,984],{},[42,975,976],{},[45,977,978,981],{},[48,979,980],{},"Aspekt",[48,982,983],{},"Empfehlung",[58,985,986,996,1006,1016,1026],{},[45,987,988,993],{},[63,989,990],{},[18,991,992],{},"API-Token-Speicherung",[63,994,995],{},"Environment-Variablen oder Secret Manager, nie im Code",[45,997,998,1003],{},[63,999,1000],{},[18,1001,1002],{},"Logging",[63,1004,1005],{},"Keine PII (Personenbezogene Daten) in Logs speichern",[45,1007,1008,1013],{},[63,1009,1010],{},[18,1011,1012],{},"Zugriffsrechte",[63,1014,1015],{},"Service-Accounts mit minimalen Rechten (Least Privilege)",[45,1017,1018,1023],{},[63,1019,1020],{},[18,1021,1022],{},"Rate-Limiting",[63,1024,1025],{},"Respektieren, um keine Systemüberlastung zu verursachen",[45,1027,1028,1033],{},[63,1029,1030],{},[18,1031,1032],{},"Pseudonymisierung",[63,1034,1035],{},"Bei Weiterleitung an externe Systeme (z. B. KI-APIs)",[31,1037],{},[34,1039,1041],{"id":1040},"vergleich-wann-welches-system","Vergleich: Wann welches System?",[39,1043,1044,1057],{},[42,1045,1046],{},[45,1047,1048,1051,1054],{},[48,1049,1050],{},"Use Case",[48,1052,1053],{},"Empfohlener Einstieg",[48,1055,1056],{},"Anmerkung",[58,1058,1059,1072,1086,1097,1110],{},[45,1060,1061,1064,1069],{},[63,1062,1063],{},"Neues Python-Projekt, Zammad",[63,1065,1066,1068],{},[82,1067,121],{}," + REST als Fallback",[63,1070,1071],{},"Community-Client reicht für 80%",[45,1073,1074,1077,1083],{},[63,1075,1076],{},"OTOBO\u002FZnuny-Integration",[63,1078,1079,1080,1082],{},"Eigener ",[82,1081,84],{},"-Wrapper",[63,1084,1085],{},"GenericInterface-Doku nutzen",[45,1087,1088,1091,1094],{},[63,1089,1090],{},"Multi-System-Automation",[63,1092,1093],{},"Open Ticket AI Framework",[63,1095,1096],{},"Einheitliches Datenmodell",[45,1098,1099,1102,1107],{},[63,1100,1101],{},"Prototyping\u002FSchnelltests",[63,1103,1104,1105],{},"Jupyter Notebook + ",[82,1106,84],{},[63,1108,1109],{},"Direktes API-Exploration",[45,1111,1112,1115,1122],{},[63,1113,1114],{},"Produktive Cronjobs",[63,1116,1117,1118,1121],{},"Python-Skript + ",[82,1119,1120],{},"python-dotenv"," + Logging",[63,1123,1124],{},"Robustheit vor Eleganz",[31,1126],{},[34,1128,1130],{"id":1129},"fazit-und-nächste-schritte","Fazit und nächste Schritte",[14,1132,1133,1134],{},"Python-Automation für Ticket-Systeme ist kein „entweder-oder“ zwischen Client-Libraries und direkter REST-Nutzung. Die meisten Teams profitieren von einem ",[18,1135,1136],{},"Hybrid-Ansatz:",[612,1138,1139,1145,1151],{},[411,1140,1141,1144],{},[18,1142,1143],{},"Offizielle\u002FCommunity-Libraries"," für Standardoperationen",[411,1146,1147,1150],{},[18,1148,1149],{},"Direkte REST-Calls"," für Edge Cases und Debugging",[411,1152,1153,1156],{},[18,1154,1155],{},"Eigene Abstraktionsschicht"," für Cross-System-Konsistenz",[14,1158,1159,1160,1164],{},"Wer tiefer einsteigen möchte, findet in unseren ",[414,1161,1163],{"href":1162},"\u002Fzammad-plugins","Plugin-Verzeichnissen"," für Zammad, OTOBO und Znuny passende Erweiterungen, die oft selbst Python-Integrationen mitbringen.",[31,1166],{},[14,1168,1169],{},[1170,1171,1172],"em",{},"Dieser Artikel ist ein technisches Praxis-Summary. Für aktuelle API-Versionen und Breaking Changes konsultieren Sie die jeweiligen offiziellen Dokumentationen.",[1174,1175,1176],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":129,"searchDepth":142,"depth":142,"links":1178},[1179,1180,1185,1189,1194,1195,1196,1197],{"id":36,"depth":142,"text":37},{"id":109,"depth":142,"text":110,"children":1181},[1182,1183],{"id":114,"depth":149,"text":115},{"id":285,"depth":149,"text":1184},"Direct REST-API mit requests",{"id":431,"depth":142,"text":432,"children":1186},[1187,1188],{"id":442,"depth":149,"text":443},{"id":606,"depth":149,"text":607},{"id":653,"depth":142,"text":654,"children":1190},[1191,1192,1193],{"id":657,"depth":149,"text":658},{"id":736,"depth":149,"text":737},{"id":806,"depth":149,"text":807},{"id":861,"depth":142,"text":862},{"id":967,"depth":142,"text":968},{"id":1040,"depth":142,"text":1041},{"id":1129,"depth":142,"text":1130},"2026-05-20","Überblick über Python-Clients und REST-API-Integrationen für Zammad, OTOBO und Znuny – von offiziellen Libraries bis zu eigenen Automatisierungs-Skripten.",false,"md",{"language":1203},"de","\u002Fblog\u002Fpython-automation-ticketsysteme-rest-api",{"title":5,"description":1199},"blog\u002Fpython-automation-ticketsysteme-rest-api","WO9HyG1klLIKRAZXh6N3WgSvOoWgDPjEi6z3G-X-New",1780063714664]