[{"data":1,"prerenderedAt":743},["ShallowReactive",2],{"nav_featured_podcast":3,"nav_featured_press_release":21,"navigation_content":30,"engineering_blog_post:migrating-gh-boards":378},{"uid":4,"title":5,"episodeLabel":6,"dateLabel":7,"episodeArt":8,"href":20},"from-payment-rails-to-connectivity","From Payment Rails to Connectivity, with The Clearing House's David Watson","EPISODE 5","August 18, 2026",{"dimensions":9,"alt":12,"copyright":13,"url":14,"id":15,"edit":16},{"width":10,"height":11},1064,652,"Payments Cannot Fail — Episode 05 with David Watson",null,"\u002F_prismic-media\u002Fd80df2e47c3ecb6f-cFcyPAgzKCMQEaND_Episode-05-David.jpg","cFcyPAgzKCMQEaND",{"x":17,"y":17,"zoom":18,"background":19},0,1,"transparent","\u002Fresources\u002Fpodcasts\u002Ffrom-payment-rails-to-connectivity",{"uid":22,"title":23,"tag":24,"dateLabel":25,"imageUrl":26,"imageAlt":27,"href":28,"imageFit":29},"form3-expands-strategic-partnership-with-sumup","Form3 expands strategic partnership with SumUp to bring real-time SEPA payments to millions of European small businesses","EU","February 25, 2026","\u002F_prismic-media\u002F3e91177f91478610-QA44Gzev0_tdg3ZI_SumUp.jpg","sumup","\u002Fresources\u002Fpress-releases\u002Fform3-expands-strategic-partnership-with-sumup","cover",{"header":31,"platform":59,"solutions":91,"developers":133,"company":155,"resources":184,"partnerships":208,"footer":220,"mobile":325},{"ctaLabel":32,"ctaHref":33,"topNav":34},"TALK TO OUR TEAM","\u002Fcontact",[35,39,43,47,51,55],{"label":36,"href":37,"key":38},"Platform","\u002Fplatform","platform",{"label":40,"href":41,"key":42},"Solutions","\u002Fsolutions","solutions",{"label":44,"href":45,"key":46},"Developers","\u002Fdevelopers","developers",{"label":48,"href":49,"key":50},"Company","\u002Fcompany","company",{"label":52,"href":53,"key":54},"Resources","\u002Fresources","resources",{"label":56,"href":57,"key":58},"Partnerships","\u002Fpartnerships","partnerships",{"sectionEyebrow":60,"primary":61,"productLinks":65,"enterprise":81,"trustFabric":86},"Explore the Platform",{"heading":62,"body":63,"ctaLabel":64,"ctaHref":37},"Our Payments Platform","A resilient, cloud-native platform powering real-time account-to-account payments at global scale.","See How It Works",[66,69,72,75,78],{"label":67,"href":68},"Payments Processing & Gateway","\u002Fpayments-processing-gateway",{"label":70,"href":71},"Orchestration & Intelligent Routing","\u002Forchestration-intelligent-routing",{"label":73,"href":74},"Payments Simulator","\u002Fpayments-simulator",{"label":76,"href":77},"Sponsor Bank Enablement","\u002Fsponsor-bank-enablement",{"label":79,"href":80},"Informed UI","\u002Finformed-ui",{"heading":82,"body":83,"ctaLabel":84,"ctaHref":85},"Form3 Enterprise","Deploy and manage proven cloud-native payments technology in the environment that works for your institution.","Deploy Enterprise","\u002Fplatform\u002Fenterprise",{"heading":87,"body":88,"ctaLabel":89,"ctaHref":90},"Form3 Trust Fabric","Manage TLS certificates, cryptographic vulnerabilities and quantum readiness at scale, with continuous visibility.","Deploy Trust Fabric","\u002Fplatform\u002Ftrust-fabric",{"regionsIntro":92,"regions":98,"industries":118},{"eyebrow":93,"heading":94,"headingLine2":95,"body":96,"ctaLabel":64,"ctaHref":97},"Regions","Global Reach.","Regional Expertise.","A proven payments platform serving organizations worldwide, with specialized experience across North America, United Kingdom, and Europe.","\u002Fglobal",[99,108,113],{"label":100,"description":101,"ctaLabel":102,"href":103,"extraLinks":104},"North America","Real-time infrastructure, through a resilient, multi-cloud platform built for scale and regulatory confidence.","US HOME","\u002Fsolutions\u002Funited-states",[105],{"label":106,"href":107},"CANADA HOME","\u002Fsolutions\u002Fcanada",{"label":109,"description":110,"ctaLabel":111,"href":112},"United Kingdom","Powering account-to-account payments at the heart of the UK financial ecosystem, with proven resilience.","UK HOME","\u002Fsolutions\u002Funited-kingdom",{"label":114,"description":115,"ctaLabel":116,"href":117},"Europe","Enabling secure, real-time and SEPA payments across European markets through a unified, cloud-native platform.","EUROPE HOME","\u002Fsolutions\u002Feurope",{"eyebrow":119,"links":120},"Industries",[121,125,129],{"label":122,"href":123,"iconSrc":124},"Financial Services","\u002Fsolutions\u002Findustries\u002Ffinancial-services","\u002F_prismic-media\u002F29c09c45caa89db9-kbUHZmgTmuIMbo1t_FinancialServices_menu.svg",{"label":126,"href":127,"iconSrc":128},"Fintech","\u002Fsolutions\u002Findustries\u002Ffintech","\u002F_prismic-media\u002F6de3e7cb4dd8d874-SjwNe6Nh-bbuTi0P_Fintech_menu.svg",{"label":130,"href":131,"iconSrc":132},"Government","\u002Fsolutions\u002Findustries\u002Fgovernment","\u002F_prismic-media\u002Fe71bc3a74a1d2142-kaGgdituzKDnVTbm_Government_menu.svg",{"engineering":134,"api":142,"pressEyebrow":154},{"eyebrow":135,"links":136},"Engineering",[137,139],{"label":135,"href":138},"\u002Fengineering",{"label":140,"href":141},"Accreditations","\u002Faccreditations",{"eyebrow":143,"links":144},"API",[145,148,151],{"label":146,"href":147},"API Docs","https:\u002F\u002Fwww.api-docs.form3.tech\u002F",{"label":149,"href":150},"API Tutorials","https:\u002F\u002Fwww.api-docs.form3.tech\u002Fapi\u002Ftutorials\u002Fgetting-started\u002Fintroduction",{"label":152,"href":153},"API Status","https:\u002F\u002Fstatus.form3.tech\u002F","Latest Press Release",{"about":156,"people":171,"podcastEyebrow":183},{"eyebrow":157,"links":158},"About",[159,162,165,168],{"label":160,"href":161},"Our Story","\u002Fcompany\u002Four-story",{"label":163,"href":164},"Our Team","\u002Fcompany\u002Fteam",{"label":166,"href":167},"Our Success & Strategic Approach","\u002Fcompany\u002Four-success",{"label":169,"href":170},"Customer Stories","\u002Fcompany\u002Fcustomer-stories",{"eyebrow":172,"links":173},"People",[174,177,180],{"label":175,"href":176},"Careers","\u002Fcompany\u002Fcareers",{"label":178,"href":179},"Vacancies","\u002Fcompany\u002Fvacancies",{"label":181,"href":182},"Culture","\u002Fcompany\u002Fculture","Latest Podcast",{"intro":185,"links":189,"pressEyebrow":154,"podcastEyebrow":183},{"eyebrow":52,"heading":186,"body":187,"ctaLabel":188,"ctaHref":53},"Form3 Resources","Our Resources bring together thought leadership, engineering insights, payments expertise, webinars, podcasts, whitepapers, company news and stories from our teams—all in one place.","LEARN MORE",[190,193,196,199,202,205],{"label":191,"href":192},"Payments Cannot Fail Podcast","\u002Fresources\u002Fpodcasts",{"label":194,"href":195},"Press Releases","\u002Fresources\u002Fpress-releases",{"label":197,"href":198},"Payment Insights","\u002Fresources\u002Fpayment-insights",{"label":200,"href":201},"Culture Blog","\u002Fresources\u002Fculture-blog",{"label":203,"href":204},"Engineering Blog","\u002Fresources\u002Fengineering-blog",{"label":206,"href":207},"Events","\u002Fevents",{"intro":209,"links":213,"pressEyebrow":154},{"eyebrow":56,"heading":210,"body":211,"ctaLabel":212,"ctaHref":57},"Partner with Form3","Explore how Form3 works with banks, fintechs, cloud providers, technology platforms and system integrators to accelerate payment modernization, strengthen customer offerings and unlock new opportunities for growth.","Let's Partner Together",[214,217],{"label":215,"href":216},"Technical & Ecosystem Partners","\u002Fpartnerships\u002Ftechnical-ecosystem",{"label":218,"href":219},"Channel & Distribution Partners","\u002Fpartnerships\u002Fchannel-distribution",{"platform":221,"region":229,"development":240,"company":248,"resources":259,"partnerships":267,"stayConnected":272,"legalLinks":292,"certifications":314},{"title":36,"links":222},[223,224,225,226,227,228],{"label":62,"href":37},{"label":67,"href":68},{"label":70,"href":71},{"label":73,"href":74},{"label":76,"href":77},{"label":79,"href":80},{"title":230,"links":231},"Region",[232,234,236,237,238],{"label":233,"href":97},"Global",{"label":235,"href":103},"United States",{"label":109,"href":112},{"label":114,"href":117},{"label":239,"href":107},"Canada",{"title":241,"links":242},"Development",[243,244,245,246,247],{"label":135,"href":138},{"label":140,"href":141},{"label":146,"href":147},{"label":149,"href":150},{"label":152,"href":153},{"title":48,"links":249},[250,251,252,253,254,255,256],{"label":160,"href":161},{"label":163,"href":164},{"label":169,"href":170},{"label":175,"href":176},{"label":178,"href":179},{"label":181,"href":182},{"label":257,"href":258},"Net Zero","\u002Fcompany\u002Fnet-zero",{"title":52,"links":260},[261,262,263,264,265,266],{"label":186,"href":53},{"label":191,"href":192},{"label":194,"href":195},{"label":200,"href":201},{"label":203,"href":204},{"label":206,"href":207},{"title":56,"links":268},[269,270,271],{"label":210,"href":57},{"label":215,"href":216},{"label":218,"href":219},{"title":273,"body":274,"ctaLabel":32,"ctaHref":33,"social":275},"Stay Connected","Form3 is a global account-to-account financial fabric delivering a proven payments platform trusted by banks and fintechs to deliver resilient, real-time payment experiences for their customers at scale.",[276,280,284,288],{"label":277,"href":278,"icon":279},"LinkedIn","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fform3-financial-cloud\u002F","linkedin",{"label":281,"href":282,"icon":283},"X","https:\u002F\u002Fwww.x.com\u002FForm3Cloud","x",{"label":285,"href":286,"icon":287},"YouTube","https:\u002F\u002Fwww.youtube.com\u002F@Form3Cloud","youtube",{"label":289,"href":290,"icon":291},"Instagram","https:\u002F\u002Fwww.instagram.com\u002Fform3.tech","instagram",[293,296,299,302,305,308,311],{"label":294,"href":295},"Terms & Conditions","\u002Flegal\u002Fterms-and-conditions",{"label":297,"href":298},"Privacy Policy","\u002Flegal\u002Fdata-privacy-statement",{"label":300,"href":301},"Recruitment Data Policy","\u002Flegal\u002Frecruitment-data-policy",{"label":303,"href":304},"Modern Slavery Statement","\u002Flegal\u002Fmodern-slavery",{"label":306,"href":307},"License","\u002Flegal\u002Flicense",{"label":309,"href":310},"Cookie Policy","\u002Flegal\u002Fcookie-policy",{"label":312,"href":313},"Manage Cookies","#cookies",[315,317,319,321,323],{"label":316},"ISO 27001",{"label":318},"ISO 27017",{"label":320},"ISO 27018",{"label":322},"ISAE 3000 SOC 2 Type II",{"label":324},"ISO 22301",{"platform":326,"solutions":341,"developers":355,"company":365,"resources":366,"partnerships":374},[327,329,339,340],{"kind":328,"label":62,"href":37},"link",{"kind":330,"label":60,"children":331},"group",[332,333,334,335,337],{"label":67,"href":68},{"label":70,"href":71},{"label":73,"href":74},{"label":76,"href":336},"\u002Fplatform\u002Fsponsor-bank-enablement",{"label":79,"href":338},"\u002Fplatform\u002Finform3d-ui",{"kind":328,"label":82,"href":85},{"kind":328,"label":87,"href":90},[342,344,350],{"kind":328,"label":233,"href":343},"\u002Fsolutions\u002Fglobal",{"kind":330,"label":93,"children":345},[346,347,348,349],{"label":235,"href":103},{"label":239,"href":107},{"label":109,"href":112},{"label":114,"href":117},{"kind":330,"label":119,"children":351},[352,353,354],{"label":122,"href":123},{"label":126,"href":127},{"label":130,"href":131},[356,360],{"kind":330,"label":135,"children":357},[358,359],{"label":135,"href":138},{"label":140,"href":141},{"kind":330,"label":143,"children":361},[362,363,364],{"label":146,"href":147},{"label":149,"href":150},{"label":152,"href":153},[],[367,368,369,370,371,372,373],{"kind":328,"label":186,"href":53},{"kind":328,"label":191,"href":192},{"kind":328,"label":194,"href":195},{"kind":328,"label":197,"href":198},{"kind":328,"label":200,"href":201},{"kind":328,"label":203,"href":204},{"kind":328,"label":206,"href":207},[375,376,377],{"kind":328,"label":210,"href":57},{"kind":328,"label":215,"href":216},{"kind":328,"label":218,"href":219},{"id":379,"uid":380,"url":381,"type":382,"href":383,"tags":384,"first_publication_date":385,"last_publication_date":386,"slugs":387,"linked_documents":389,"lang":390,"alternate_languages":391,"data":392},"alz00xEAAC0AUWac","migrating-gh-boards","\u002Fresources\u002Fengineering-blog\u002Fmigrating-gh-boards","engineering_blog_post","https:\u002F\u002Fform3-website.cdn.prismic.io\u002Fapi\u002Fv2\u002Fdocuments\u002Fsearch?ref=apnMZxEAACcAKWxv&q=%5B%5B%3Ad+%3D+at%28document.id%2C+%22alz00xEAAC0AUWac%22%29+%5D%5D",[],"2026-07-19T16:21:58+0000","2026-08-27T02:06:59+0000",[388],"why-would-you-want-to-do-that",[],"en-us",[],{"title":393,"excerpt":394,"card_image":395,"published_date":402,"reading_time":403,"tag":404,"dek":394,"featured_image":405,"about_form3":412,"client_about_heading":13,"client_about_body":413,"author_name":414,"author_title":415,"author_photo":416,"author_bio":423,"author_linkedin":436,"slices":439,"meta_title":393,"meta_description":394},"Migrating project v2 boards in GitHub","Once upon a time, there were project boards in GitHub. They helped you plan, they looked like Trello, and they were much loved. They were classic. Then, one day, they were deprecated! Along came project v2 boards. They were like Trello, but also like a spreadsheet, and much more between, and they became the new project planning tool in GitHub. This post is about migrating project boards in GitHub. It's not, as you might expect, about migrating from classic project boards to v2 projects. GitHub offer a migration tool for that in their UI, and it's easy to do. Instead, this post is about migrating from one v2 board to another.",{"dimensions":396,"alt":393,"copyright":13,"url":398,"id":399,"edit":400},{"width":397,"height":397},800,"\u002F_prismic-media\u002Fdfe6cc73fc80b23b--k-SRcoga9nP6phQ_migrating-gh-boards.svg","-k-SRcoga9nP6phQ",{"x":17,"y":17,"zoom":18,"background":401},"#ffffff","2023-02-22",5,"Blogs",{"dimensions":406,"alt":13,"copyright":13,"url":409,"id":410,"edit":411},{"width":407,"height":408},1200,669,"\u002F_prismic-media\u002Fb3d284b415eccdce-TxE896nUniwYhPzD_5bb68ae8-3608-4127-be91-01bd61e.jpg","TxE896nUniwYhPzD",{"x":17,"y":17,"zoom":18,"background":401},[],[],"Andy Kuszyk","Staff Engineer",{"dimensions":417,"alt":414,"copyright":13,"url":420,"id":421,"edit":422},{"width":418,"height":419},317,473,"\u002F_prismic-media\u002Fc371d17a59432918-GFOBoL2Tovd3zPoj_27d09fad-dd24-483c-92fc-1ac2a07.jpeg","GFOBoL2Tovd3zPoj",{"x":17,"y":17,"zoom":18,"background":401},[424],{"type":425,"text":426,"spans":427},"paragraph","Andy Kuszyk is a Staff Engineer at Form3, based in Southampton. He's been working as a software engineer for 8 years with a variety of technologies, including .NET, Python and most recently Go. Check out more of his tech articles on his blog.",[428],{"start":429,"end":430,"type":431,"data":432},233,241,"hyperlink",{"link_type":433,"url":434,"target":435},"Web","http:\u002F\u002Fandykuszyk.github.io\u002F","_blank",{"link_type":433,"key":437,"url":438,"target":435},"e1c88d6b-6263-4727-bb1b-7b3f7d058448","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fandy-kuszyk",[440,455,485,496,506,514,522,571,585,593,607,615,626,634,655,663,671,679,693,724,732],{"variation":441,"version":442,"items":443,"primary":444,"id":453,"slice_type":454,"slice_label":13},"default","initial",[],{"body":445},[446,450],{"type":447,"text":448,"spans":449},"heading2","Why would you want to do that?",[],{"type":425,"text":451,"spans":452},"Good question! Well, the GitHub classic to v2 migration tool migrates issues to a v2 board with the same status names\u002Fcolumns as the classic board. If you want to migrate the issues on this new board into a different v2 board, or you just want to combine multiple v2 project boards into one, you'll need to work outside of the GitHub UI.",[],"rich_text$320fb916-da98-4bc4-bf5f-0e5df85993e4","rich_text",{"variation":441,"version":442,"items":456,"primary":457,"id":484,"slice_type":454,"slice_label":13},[],{"body":458},[459,462,465,474,481],{"type":447,"text":460,"spans":461},"So, how do you do it?",[],{"type":425,"text":463,"spans":464},"In this blog post, I'm going to show you how I did this using a combination of:",[],{"type":466,"text":467,"spans":468},"list-item","The GitHub GraphQL API.",[469],{"start":470,"end":471,"type":431,"data":472},4,22,{"link_type":433,"url":473},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Fgraphql\u002Freference",{"type":466,"text":475,"spans":476},"The GitHub command line interface.",[477],{"start":470,"end":478,"type":431,"data":479},33,{"link_type":433,"url":480},"https:\u002F\u002Fgithub.com\u002Fcli\u002Fcli",{"type":466,"text":482,"spans":483},"A little bit of Bash and Python scripting.",[],"rich_text$44044b55-a3a8-41a5-97b3-b387338f0383",{"variation":441,"version":442,"items":486,"primary":487,"id":495,"slice_type":454,"slice_label":13},[],{"body":488},[489,492],{"type":447,"text":490,"spans":491},"Step 1: list the issues on the project v2 board you want to migrate",[],{"type":425,"text":493,"spans":494},"This can be achieved with the GitHub GraphQL API, via the GitHub CLI:",[],"rich_text$cc630263-420b-4be6-9cbf-2d210463a7e5",{"variation":441,"version":442,"items":497,"primary":498,"id":504,"slice_type":505,"slice_label":13},[],{"language_label":13,"code":499},[500],{"type":501,"text":502,"spans":503},"preformatted","gh api graphql \\\n    --paginate \\\n    --jq '.data.organization.projectV2.items.nodes[]' \\\n    -f query='\nquery($endCursor: String) {\n  organization(login:\"\u003Corganisation-id>\"){\n    projectV2(number:\u003Cproject-number>){\n      items(first:10, after: $endCursor){\n        pageInfo{ hasNextPage endCursor }\n        nodes{\n          fieldValueByName(name:\"Status\"){\n            __typename\n            ... on ProjectV2ItemFieldSingleSelectValue{\n              name\n            }\n          }\n          content{\n            __typename\n            ... on Issue{\n              number\n              title\n              id\n            }\n          }\n        }\n      }\n    }\n  }\n}' | jq | sed 's\u002F^}$\u002F},\u002Fg' | sed '1s\u002F^{$\u002F[{\u002Fg' | sed '$s\u002F^},$\u002F}]\u002Fg' | tee issues.json",[],"code_block$c7645222-a813-451d-a527-88d6ba2f1bd6","code_block",{"variation":441,"version":442,"items":507,"primary":508,"id":513,"slice_type":454,"slice_label":13},[],{"body":509},[510],{"type":425,"text":511,"spans":512},"This results in some output like this:",[],"rich_text$794f9ab2-fa27-40b8-bd5e-d2ea1b9f0ee9",{"variation":441,"version":442,"items":515,"primary":516,"id":521,"slice_type":505,"slice_label":13},[],{"language_label":13,"code":517},[518],{"type":501,"text":519,"spans":520},"[{\n  \"content\": {\n    \"__typename\": \"Issue\",\n    \"id\": \"I_kwDOGolhjhjkhjke\",\n    \"number\": 112,\n    \"title\": \"Handle thing field correctly in blah\"\n  },\n  \"fieldValueByName\": {\n    \"__typename\": \"ProjectV2ItemFieldSingleSelectValue\",\n    \"name\": \"Done 🎉\"\n  }\n},\n{\n  \"content\": {\n    \"__typename\": \"Issue\",\n    \"id\": \"I_kwDOGhjkhkjhjkkN\",\n    \"number\": 234,\n    \"title\": \"Add a stage to the CI which runs the load tests\"\n  },\n  \"fieldValueByName\": {\n    \"__typename\": \"ProjectV2ItemFieldSingleSelectValue\",\n    \"name\": \"Done 🎉\"\n  }\n}]",[],"code_block$6fade4a6-e1c8-4a1f-ab99-c33f7d778db7",{"variation":441,"version":442,"items":523,"primary":524,"id":570,"slice_type":454,"slice_label":13},[],{"body":525},[526,529,535,546,551,559,567],{"type":425,"text":527,"spans":528},"Let's just examine the commands in the pipeline:",[],{"type":466,"text":530,"spans":531},"gh api graphql makes the raw request to GitHub, but outputs paginated data that isn't well-formed in a JSON array.",[532],{"start":17,"end":533,"type":534},14,"em",{"type":466,"text":536,"spans":537},"jq pretty-formats the JSON, which leaves characters like { and } on their own line, although still not in a JSON array.",[538,540,543],{"start":17,"end":539,"type":534},2,{"start":541,"end":542,"type":534},57,58,{"start":544,"end":545,"type":534},63,64,{"type":466,"text":547,"spans":548},"sed 's\u002F^}$\u002F},\u002Fg' replaces errant closing braces of array elements with a well formed brace and comma. This makes paginated elements into valid elements of a JSON array.",[549],{"start":17,"end":550,"type":534},16,{"type":466,"text":552,"spans":553},"sed '1s\u002F^{$\u002F[{\u002Fg' replaces the first line containing an opening brace with [{, which opens a well-formed JSON array.",[554,556],{"start":17,"end":555,"type":534},17,{"start":557,"end":558,"type":534},75,77,{"type":466,"text":560,"spans":561},"sed '$s\u002F^},$\u002F}]\u002Fg' replaces the final line containing a trailing comma, with a well-formed }], which closes the JSON array.",[562,564],{"start":17,"end":563,"type":534},18,{"start":565,"end":566,"type":534},91,93,{"type":425,"text":568,"spans":569},"The result is a nicely formatted JSON array containing an element for every issue on the board, along with its current status.",[],"rich_text$bf539717-1971-49fc-8000-97dcdb8096ac",{"variation":441,"version":442,"items":572,"primary":573,"id":584,"slice_type":454,"slice_label":13},[],{"body":574},[575,578,581],{"type":447,"text":576,"spans":577},"Step 2: map the current statuses, to statuses on your target board",[],{"type":425,"text":579,"spans":580},"Now that we've got all the issues and their current statuses, we can map all their statuses to statuses on your new project board. This is the step that allows you to migrate issues from one board to another, when there isn't necessarily a straightforward mapping from the statuses on one board to the other.",[],{"type":425,"text":582,"spans":583},"This is hard to do in Bash, but relatively straight forward in Python:",[],"rich_text$bd9dd29e-024d-4d96-84b6-514053b903ac",{"variation":441,"version":442,"items":586,"primary":587,"id":592,"slice_type":505,"slice_label":13},[],{"language_label":13,"code":588},[589],{"type":501,"text":590,"spans":591},"import json\nimport os\nimport sys\n\n# This function maps statuses on your source board to those on your\n# target board.\ndef map_old_status_to_new(old_status):\n    if \"Backlog\" in old_status:\n        return \"Product planning\"\n    if \"Ready To Start\" in old_status:\n        return \"Ready to develop\"\n    if \"In Progress\" in old_status:\n        return \"In development\"\n    if \"Done\" in old_status:\n        return None\n    if \"Parked\" in old_status:\n        return None\n\n# Read the issues data from file.\nwith open('issues.json') as f:\n\tissues = json.loads(f.read())\n\n# Iterate over each of the issues, and map the old status to their\n# new status.\nfor issue in issues:\n    try:\n        number = issue[\"content\"][\"number\"]\n        old_status = issue[\"fieldValueByName\"][\"name\"]\n        new_status = map_old_status_to_new(old_status)\n        if new_status is None:\n            print(\n                \"ignoring issue {} because its status is {}\".format(\n                    number,\n                    old_status\n                ),\n                file=sys.stderr\n            )\n            continue\n\t\t# Output some new JSON representing the issue and its mapped status.\n        mapped_issue = {\n            \"number\": number,\n            \"id\": issue[\"content\"][\"id\"],\n            \"old_status\": old_status,\n            \"new_status\": new_status\n        }\n        print(json.dumps(mapped_issue))\n    except Exception as e:\n        print(\n            \"error processing issue. error: {}, issue: {}\".format(e, issue),\n            file=sys.stderr\n        )",[],"code_block$9fe4a61f-f38f-458d-95a1-63226bc1fd4b",{"variation":441,"version":442,"items":594,"primary":595,"id":606,"slice_type":454,"slice_label":13},[],{"body":596},[597,600,603],{"type":425,"text":598,"spans":599},"This script reads your issues data, and re-shapes the data into a JSON object per line of standard output. Each object contains the information required to add the issue to the new project board in the correct status.",[],{"type":425,"text":601,"spans":602},"You could run this Python script from a shell and pipe its output to subsequent commands, or save the output to a file.",[],{"type":425,"text":604,"spans":605},"The output looks something like this:",[],"rich_text$bd42144b-f617-4b52-85db-e546ddc2815d",{"variation":441,"version":442,"items":608,"primary":609,"id":614,"slice_type":505,"slice_label":13},[],{"language_label":13,"code":610},[611],{"type":501,"text":612,"spans":613},"{\"number\": 1151, \"id\": \"I_kwhjkol6Is5ZStTQ\", \"old_status\": \"In Progress \\ud83c\\udfd7\\ufe0f\", \"new_status\": \"In development\"}\n{\"number\": 902, \"id\": \"I_kwhjkol6Is5U9_wD\", \"old_status\": \"In Progress \\ud83c\\udfd7\\ufe0f\", \"new_status\": \"In development\"}\n{\"number\": 1229, \"id\": \"I_kwhjkol6Is5aw2yu\", \"old_status\": \"In Progress \\ud83c\\udfd7\\ufe0f\", \"new_status\": \"In development\"}",[],"code_block$05b05a21-1133-43ba-aa47-5227a46b7c07",{"variation":441,"version":442,"items":616,"primary":617,"id":625,"slice_type":454,"slice_label":13},[],{"body":618},[619,622],{"type":447,"text":620,"spans":621},"Step 3: get the ID of the new project",[],{"type":425,"text":623,"spans":624},"In order to use the GitHub API to add the issues to the new project, we'll need its ID. We can get this easily using the GitHub CLI:",[],"rich_text$8d972261-789e-4dde-926b-30ffb92049e8",{"variation":441,"version":442,"items":627,"primary":628,"id":633,"slice_type":505,"slice_label":13},[],{"language_label":13,"code":629},[630],{"type":501,"text":631,"spans":632},"project_id=$(gh api graphql --jq '.data.organization.projectV2.id' -f query='\n  query{\n\torganization(login:\"form3tech\"){\n\t  projectV2(number:345){\n\t    id\n\t  }\n\t}\n  }')",[],"code_block$f5feb686-803e-42dd-967a-864f85ceabcb",{"variation":441,"version":442,"items":635,"primary":636,"id":654,"slice_type":454,"slice_label":13},[],{"body":637},[638,641,644,648,651],{"type":447,"text":639,"spans":640},"Step 4: find out about the status field in the new project",[],{"type":425,"text":642,"spans":643},"Project v2 boards have a more flexible status configuration than the columns in a classic board, so in order to make use of them via the API you need to know:",[],{"type":645,"text":646,"spans":647},"o-list-item","The ID of the status field on issues.",[],{"type":645,"text":649,"spans":650},"The IDs and names of the different status options.",[],{"type":425,"text":652,"spans":653},"We can find all this out in one go with a single request to the GitHub API:",[],"rich_text$531d26f4-1b9c-496d-b798-9207fb6d0cbd",{"variation":441,"version":442,"items":656,"primary":657,"id":662,"slice_type":505,"slice_label":13},[],{"language_label":13,"code":658},[659],{"type":501,"text":660,"spans":661},"gh api graphql -f query='\n  query{\n\torganization(login: \"form3tech\"){\n\t  projectV2(number: 345) {\n\t    field(name:\"Status\"){\n\t      __typename\n\t      ... on ProjectV2SingleSelectField{\n \t\t    id\n\t\t    options{\n\t\t      id\n\t\t      name\n\t\t    }\n\t      }\n\t    }\n\t  }\n\t}\n  }' | jq",[],"code_block$3a9907ce-1e62-4cb9-bbea-867e822c5373",{"variation":441,"version":442,"items":664,"primary":665,"id":670,"slice_type":454,"slice_label":13},[],{"body":666},[667],{"type":425,"text":668,"spans":669},"The output is something like this:",[],"rich_text$efd46e5c-2ab1-4ba5-ba4e-3a8eb5197256",{"variation":441,"version":442,"items":672,"primary":673,"id":678,"slice_type":505,"slice_label":13},[],{"language_label":13,"code":674},[675],{"type":501,"text":676,"spans":677},"{\n  \"data\": {\n    \"organization\": {\n      \"projectV2\": {\n        \"field\": {\n          \"__typename\": \"ProjectV2SingleSelectField\",\n          \"id\": \"PVTSSF_lhjkerhkjhjkJlw9zgF9whc\",\n          \"options\": [\n            {\n              \"id\": \"ehjkhkj6\",\n              \"name\": \"Product planning\"\n            },\n            {\n              \"id\": \"hjkhkje4\",\n              \"name\": \"Ready to develop\"\n            },\n            {\n              \"id\": \"hukjhkjh\",\n              \"name\": \"In development\"\n            },\n            {\n              \"id\": \"dhjkhk31\",\n              \"name\": \"Ready for demo\"\n            },\n            {\n              \"id\": \"4hjkhkcb\",\n              \"name\": \"Blocked\"\n            },\n            {\n              \"id\": \"hjkh6657\",\n              \"name\": \"Approved\"\n            }\n          ]\n        }\n      }\n    }\n  }\n}",[],"code_block$69a80c4b-d63b-4169-93f6-c95831ce09f7",{"variation":441,"version":442,"items":680,"primary":681,"id":692,"slice_type":454,"slice_label":13},[],{"body":682},[683],{"type":425,"text":684,"spans":685},"From this output, we can find the status field ID (jq '.data.organization.projectV2.field.id' | sed 's\u002F\"\u002F\u002Fg'), and the status options (jq '.data.organization.projectV2.field.options')",[686,689],{"start":687,"end":688,"type":534},51,108,{"start":690,"end":691,"type":534},135,182,"rich_text$58b15207-6f4f-4a51-a761-871a7ef65904",{"variation":441,"version":442,"items":694,"primary":695,"id":723,"slice_type":454,"slice_label":13},[],{"body":696},[697,700,703,706,712,718],{"type":447,"text":698,"spans":699},"Step 5: migrate the issues to the new board",[],{"type":425,"text":701,"spans":702},"Now, we can bring all of this data together in a Bash script which iterates over each of the issue lines outputted by our Python script, and adds the issue to the correct status on the new board:",[],{"type":425,"text":704,"spans":705},"This script assumes that:",[],{"type":466,"text":707,"spans":708},"The output from the Python script has been saved in $issue_statuses.",[709],{"start":710,"end":711,"type":534},52,67,{"type":466,"text":713,"spans":714},"The status options have been saved in $status_option_ids.",[715],{"start":716,"end":717,"type":534},38,56,{"type":466,"text":719,"spans":720},"The ID of the project has been saved in $project_id.",[721],{"start":722,"end":687,"type":534},40,"rich_text$ea322eef-7f5b-4ca4-bc80-b0140ea784e9",{"variation":441,"version":442,"items":725,"primary":726,"id":731,"slice_type":505,"slice_label":13},[],{"language_label":13,"code":727},[728],{"type":501,"text":729,"spans":730},"set -eu -o pipefail\n\necho -n \"$issue_statuses\" | while read -r issue; do\n\t# Parse each JSON object into the variables we need.\n\tissue_id=$(echo -n \"$issue\" | jq '.id' | sed 's\u002F\"\u002F\u002Fg')\n\tissue_number=$(echo -n \"$issue\" | jq '.number')\n\tnew_status=$(echo -n \"$issue\" | jq '.new_status' | sed 's\u002F\"\u002F\u002Fg')\n\tstatus_option_id=$(echo -n \"$status_option_ids\" | jq \".[] | select(.name == \\\"$new_status\\\").id\" | sed 's\u002F\"\u002F\u002Fg')\n\n\t# Print out the variables we're using for the user to see.\n\techo \"issue id: $issue_id\"\n\techo \"issue number: $issue_number\"\n\techo \"new status: $new_status\"\n\techo \"status option id: $status_option_id\"\n\n\t# Check that we've got valid data.\n\tif [[ -z \"$issue_id\" || -z \"$issue_number\" || -z \"$new_status\" || -z \"$status_option_id\" ]]; then\n\t\techo \"invalid args\"\n\t\texit 1\n\tfi\n\n\techo \"adding issue number $issue_number to new board\"\n\n\t# First, add the issue to the new project, and store the item\u002Fcard ID in a variable.\n\titem_id=$(gh api graphql -f query='\n\t\tmutation{\n\t\t\taddProjectV2ItemById(input:{\n\t\t\t\tcontentId:\"'\"$issue_id\"'\",\n\t\t\t\tprojectId:\"'\"$project_id\"'\"\n\t\t\t}){\n\t\t\t\titem{\n\t\t\t\t\tid\n\t\t\t\t\tproject{\n\t\t\t\t\t\ttitle\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}' | jq '.data.addProjectV2ItemById.item.id' | sed 's\u002F\"\u002F\u002Fg')\n\n\techo \"moving issue number $issue_number to new status $new_status\"\n\n\t# Then, assign the correct status to the new project item\u002Fcard.\n\tgh api graphql --jq '.data.updateProjectV2ItemFieldValue.projectV2Item.fieldValueByName.name' -f query='\n\t\tmutation{\n\t\t\tupdateProjectV2ItemFieldValue(input:{\n\t\t\t\titemId:\"'\"$item_id\"'\",\n\t\t\t\tvalue:{singleSelectOptionId:\"'\"$status_option_id\"'\"},\n\t\t\t\tfieldId:\"'\"$status_field_id\"'\",\n\t\t\t\tprojectId:\"'\"$project_id\"'\",\n\t\t\t\tclientMutationId:\"status-update\"\n\t\t\t}){\n\t\t\t\tprojectV2Item{\n\t\t\t\t\tfieldValueByName(name: \"Status\"){\n\t\t\t\t\t\t__typename\n\t\t\t\t\t\t... on ProjectV2ItemFieldSingleSelectValue{\n\t\t\t\t\t\t\tname\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}'\ndone",[],"code_block$4299c2e0-4a03-4dc0-8911-c63f973c78c8",{"variation":441,"version":442,"items":733,"primary":734,"id":742,"slice_type":454,"slice_label":13},[],{"body":735},[736,739],{"type":447,"text":737,"spans":738},"Step 6: profit!",[],{"type":425,"text":740,"spans":741},"So there it is: it's a bit painful, but not possible via the GitHub UI. You need to make a few requests to the GraphQL to get the data you need, and re-shape some of the output into a format that's usable. For small project boards, you could probably do this manually, but if you need to migrate hundreds of issues then I hope you find this post useful!",[],"rich_text$6dc942c4-cc91-444a-b694-950e9c1f8c9b",1788466508434]