diff --git a/corvid/templates/tickets_base.html b/corvid/templates/tickets_base.html
index 7f1b5b6..653027d 100644
--- a/corvid/templates/tickets_base.html
+++ b/corvid/templates/tickets_base.html
@@ -282,6 +282,11 @@ textarea.desc-input {
+
+
@@ -1186,6 +1191,43 @@ document.getElementById('env-create-overlay').addEventListener('click', e => {
document.addEventListener('click', () => closeEnvDropdown());
document.getElementById('env-selector').addEventListener('click', e => e.stopPropagation());
+// ── Export / Import ──────────────────────────────────────────────────────────
+
+async function exportTickets() {
+ try {
+ const data = await api('/api/tickets/export');
+ const blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json'});
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ const ts = new Date().toISOString().slice(0, 10);
+ a.href = url;
+ a.download = `tickets-export-${ts}.json`;
+ a.click();
+ URL.revokeObjectURL(url);
+ } catch(e) {
+ toast(e.message, true);
+ }
+}
+
+async function importTickets(event) {
+ const file = event.target.files?.[0];
+ if (!file) return;
+ event.target.value = '';
+ try {
+ const text = await file.text();
+ const body = JSON.parse(text);
+ const result = await api('/api/tickets/import', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify(body),
+ });
+ toast(`Imported ${result.imported} ticket(s)`);
+ await loadEnvironments();
+ } catch(e) {
+ toast(e.message || 'Import failed', true);
+ }
+}
+
loadEnvironments();
{% endblock %}