[{"data":1,"prerenderedAt":1835},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcopy-a-template-workbook-in-sharepoint-with-microsoft-graph":3,"surround:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcopy-a-template-workbook-in-sharepoint-with-microsoft-graph":1805,"ancestors:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcopy-a-template-workbook-in-sharepoint-with-microsoft-graph":1813,"children:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcopy-a-template-workbook-in-sharepoint-with-microsoft-graph":1818,"related:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcopy-a-template-workbook-in-sharepoint-with-microsoft-graph":1819},{"id":4,"title":5,"body":6,"dateModified":1772,"datePublished":1772,"description":1773,"extension":1774,"faq":1775,"keyword":1784,"kind":1785,"meta":1786,"navigation":134,"path":1795,"seo":1796,"slug":1798,"stem":1799,"topics":1800,"type":1803,"__hash__":1804},"docs\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcopy-a-template-workbook-in-sharepoint-with-microsoft-graph\u002Findex.md","Copy a Template Workbook in SharePoint with Microsoft Graph",{"type":7,"value":8,"toc":1761},"minimark",[9,24,29,55,84,88,91,414,417,424,428,447,708,723,730,909,913,924,1233,1255,1344,1348,1351,1515,1522,1538,1628,1632,1671,1675,1696,1700,1706,1712,1718,1724,1728,1757],[10,11,12,13,17,18,23],"p",{},"Teams that standardise on a workbook template usually keep it in SharePoint: a project tracker, a monthly close checklist, a budget return with locked formulas and approved formatting. Automation then needs to stamp out copies — one per project, client or month — named consistently, saved in the right folder, with a few cells filled in. Downloading the template, editing it locally and uploading it again works, but it moves bytes through your script for no reason and can lose features your Python library does not understand. Microsoft Graph can copy the file ",[14,15,16],"em",{},"inside"," SharePoint instead. This page finds the template by path, starts the server-side copy, waits for the asynchronous operation to finish, handles a copy whose name already exists, and fills in the new workbook through the workbook API. It belongs to ",[19,20,22],"a",{"href":21},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002F","Automating Excel Online with Microsoft Graph",".",[25,26,28],"h2",{"id":27},"prerequisites","Prerequisites",[30,31,36],"pre",{"className":32,"code":33,"language":34,"meta":35,"style":35},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install requests\n","bash","",[37,38,39],"code",{"__ignoreMap":35},[40,41,44,48,52],"span",{"class":42,"line":43},"line",1,[40,45,47],{"class":46},"sMTad","pip",[40,49,51],{"class":50},"srMev"," install",[40,53,54],{"class":50}," requests\n",[10,56,57,58,61,62,65,66,69,70,74,75,79,80,83],{},"You need a Microsoft Graph access token with ",[37,59,60],{},"Files.ReadWrite.All"," or ",[37,63,64],{},"Sites.ReadWrite.All"," (or ",[37,67,68],{},"Sites.Selected"," granted on the site), and the drive ID of the document library holding the template; ",[19,71,73],{"href":72},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fauthenticate-to-microsoft-graph-with-msal-for-excel-automation\u002F","Authenticate to Microsoft Graph with MSAL for Excel Automation"," covers the token, and ",[19,76,78],{"href":77},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fread-an-excel-file-from-sharepoint-or-onedrive-into-pandas\u002F","Read an Excel File from SharePoint or OneDrive into pandas"," shows how to look up a site's document library and its drive ID. The outputs below were produced against a mocked Graph API (the ",[37,81,82],{},"responses"," library) that returns the documented response shapes, including the asynchronous copy; the code is unchanged against a real tenant.",[25,85,87],{"id":86},"step-1-find-the-template-and-the-destination-folder","Step 1 — Find the template and the destination folder",[10,89,90],{},"Address both by path, so the script reads like the library's folder structure:",[30,92,96],{"className":93,"code":94,"language":95,"meta":35,"style":35},"language-python shiki shiki-themes github-light github-dark-high-contrast","import os, time\nimport requests\nfrom urllib.parse import quote\n\nGRAPH = \"https:\u002F\u002Fgraph.microsoft.com\u002Fv1.0\"\nDRIVE = \"b!projects-drive\"\nsession = requests.Session()\nsession.headers[\"Authorization\"] = f\"Bearer {os.environ['GRAPH_TOKEN']}\"\n\ndef by_path(path):\n    r = session.get(f\"{GRAPH}\u002Fdrives\u002F{DRIVE}\u002Froot:\u002F{quote(path)}\", timeout=60)\n    r.raise_for_status()\n    return r.json()\n\ntemplate = by_path(\"Templates\u002FProject tracker template.xlsx\")\nfolder = by_path(\"Projects\u002F2026\")\nprint(\"template:\", template[\"id\"], template[\"name\"], f\"({template['size']:,} bytes)\")\nprint(\"destination folder:\", folder[\"id\"], folder[\"name\"])\n","python",[37,97,98,108,115,129,136,149,160,172,211,216,229,288,294,303,308,324,339,391],{"__ignoreMap":35},[40,99,100,104],{"class":42,"line":43},[40,101,103],{"class":102},"s-kum","import",[40,105,107],{"class":106},"skGVy"," os, time\n",[40,109,111,113],{"class":42,"line":110},2,[40,112,103],{"class":102},[40,114,54],{"class":106},[40,116,118,121,124,126],{"class":42,"line":117},3,[40,119,120],{"class":102},"from",[40,122,123],{"class":106}," urllib.parse ",[40,125,103],{"class":102},[40,127,128],{"class":106}," quote\n",[40,130,132],{"class":42,"line":131},4,[40,133,135],{"emptyLinePlaceholder":134},true,"\n",[40,137,139,143,146],{"class":42,"line":138},5,[40,140,142],{"class":141},"sP0c6","GRAPH",[40,144,145],{"class":102}," =",[40,147,148],{"class":50}," \"https:\u002F\u002Fgraph.microsoft.com\u002Fv1.0\"\n",[40,150,152,155,157],{"class":42,"line":151},6,[40,153,154],{"class":141},"DRIVE",[40,156,145],{"class":102},[40,158,159],{"class":50}," \"b!projects-drive\"\n",[40,161,163,166,169],{"class":42,"line":162},7,[40,164,165],{"class":106},"session ",[40,167,168],{"class":102},"=",[40,170,171],{"class":106}," requests.Session()\n",[40,173,175,178,181,184,186,189,192,196,199,202,205,208],{"class":42,"line":174},8,[40,176,177],{"class":106},"session.headers[",[40,179,180],{"class":50},"\"Authorization\"",[40,182,183],{"class":106},"] ",[40,185,168],{"class":102},[40,187,188],{"class":102}," f",[40,190,191],{"class":50},"\"Bearer ",[40,193,195],{"class":194},"sSjpA","{",[40,197,198],{"class":106},"os.environ[",[40,200,201],{"class":50},"'GRAPH_TOKEN'",[40,203,204],{"class":106},"]",[40,206,207],{"class":194},"}",[40,209,210],{"class":50},"\"\n",[40,212,214],{"class":42,"line":213},9,[40,215,135],{"emptyLinePlaceholder":134},[40,217,219,222,226],{"class":42,"line":218},10,[40,220,221],{"class":102},"def",[40,223,225],{"class":224},"s_Opv"," by_path",[40,227,228],{"class":106},"(path):\n",[40,230,232,235,237,240,243,246,248,250,252,255,257,259,261,264,266,269,271,273,276,280,282,285],{"class":42,"line":231},11,[40,233,234],{"class":106},"    r ",[40,236,168],{"class":102},[40,238,239],{"class":106}," session.get(",[40,241,242],{"class":102},"f",[40,244,245],{"class":50},"\"",[40,247,195],{"class":194},[40,249,142],{"class":141},[40,251,207],{"class":194},[40,253,254],{"class":50},"\u002Fdrives\u002F",[40,256,195],{"class":194},[40,258,154],{"class":141},[40,260,207],{"class":194},[40,262,263],{"class":50},"\u002Froot:\u002F",[40,265,195],{"class":194},[40,267,268],{"class":106},"quote(path)",[40,270,207],{"class":194},[40,272,245],{"class":50},[40,274,275],{"class":106},", ",[40,277,279],{"class":278},"sa561","timeout",[40,281,168],{"class":102},[40,283,284],{"class":141},"60",[40,286,287],{"class":106},")\n",[40,289,291],{"class":42,"line":290},12,[40,292,293],{"class":106},"    r.raise_for_status()\n",[40,295,297,300],{"class":42,"line":296},13,[40,298,299],{"class":102},"    return",[40,301,302],{"class":106}," r.json()\n",[40,304,306],{"class":42,"line":305},14,[40,307,135],{"emptyLinePlaceholder":134},[40,309,311,314,316,319,322],{"class":42,"line":310},15,[40,312,313],{"class":106},"template ",[40,315,168],{"class":102},[40,317,318],{"class":106}," by_path(",[40,320,321],{"class":50},"\"Templates\u002FProject tracker template.xlsx\"",[40,323,287],{"class":106},[40,325,327,330,332,334,337],{"class":42,"line":326},16,[40,328,329],{"class":106},"folder ",[40,331,168],{"class":102},[40,333,318],{"class":106},[40,335,336],{"class":50},"\"Projects\u002F2026\"",[40,338,287],{"class":106},[40,340,342,345,348,351,354,357,360,363,366,368,371,373,376,379,381,384,386,389],{"class":42,"line":341},17,[40,343,344],{"class":141},"print",[40,346,347],{"class":106},"(",[40,349,350],{"class":50},"\"template:\"",[40,352,353],{"class":106},", template[",[40,355,356],{"class":50},"\"id\"",[40,358,359],{"class":106},"], template[",[40,361,362],{"class":50},"\"name\"",[40,364,365],{"class":106},"], ",[40,367,242],{"class":102},[40,369,370],{"class":50},"\"(",[40,372,195],{"class":194},[40,374,375],{"class":106},"template[",[40,377,378],{"class":50},"'size'",[40,380,204],{"class":106},[40,382,383],{"class":102},":,",[40,385,207],{"class":194},[40,387,388],{"class":50}," bytes)\"",[40,390,287],{"class":106},[40,392,394,396,398,401,404,406,409,411],{"class":42,"line":393},18,[40,395,344],{"class":141},[40,397,347],{"class":106},[40,399,400],{"class":50},"\"destination folder:\"",[40,402,403],{"class":106},", folder[",[40,405,356],{"class":50},[40,407,408],{"class":106},"], folder[",[40,410,362],{"class":50},[40,412,413],{"class":106},"])\n",[10,415,416],{},"Keep the template in a library or folder where only its owners have edit rights and everyone else can read; the automation then needs read access to the template and write access to the destination, and nobody edits the master by accident when they meant to fill in a copy. Version history in SharePoint records every change to the template itself, so a broken formula introduced last week can be traced and rolled back.",[10,418,419,420,423],{},"Resolving paths to IDs once, at the start, gives a clear error if someone has renamed the template or the folder — a 404 here is far easier to understand than one in the middle of a copy. The template and the destination can be in different drives or even different sites; the copy request below says where to put the copy with a ",[37,421,422],{},"parentReference"," holding both the destination drive ID and folder ID.",[25,425,427],{"id":426},"step-2-start-the-copy","Step 2 — Start the copy",[10,429,430,431,434,435,438,439,442,443,446],{},"A copy is a ",[37,432,433],{},"POST"," to the template's ",[37,436,437],{},"copy"," action with the destination and the new name. Graph does not do the copy during the request — it answers ",[37,440,441],{},"202 Accepted"," with a ",[37,444,445],{},"Location"," header pointing at a monitor URL:",[30,448,450],{"className":93,"code":449,"language":95,"meta":35,"style":35},"def start_copy(new_name, conflict=\"fail\"):\n    body = {\"parentReference\": {\"driveId\": DRIVE, \"id\": folder[\"id\"]}, \"name\": new_name}\n    r = session.post(f\"{GRAPH}\u002Fdrives\u002F{DRIVE}\u002Fitems\u002F{template['id']}\u002Fcopy\",\n                     params={\"@microsoft.graph.conflictBehavior\": conflict}, json=body, timeout=60)\n    if r.status_code == 409:\n        raise FileExistsError(new_name)\n    r.raise_for_status()\n    return r.headers[\"Location\"]\n\nmonitor_url = start_copy(\"Project PX-124 tracker.xlsx\")\nprint(\"202 accepted; monitor:\", monitor_url)\ntry:\n    start_copy(\"Project PX-118 tracker.xlsx\")\nexcept FileExistsError as e:\n    print(\"already exists, not copied:\", e)\n",[37,451,452,470,511,558,589,606,617,621,634,638,653,665,672,682,695],{"__ignoreMap":35},[40,453,454,456,459,462,464,467],{"class":42,"line":43},[40,455,221],{"class":102},[40,457,458],{"class":224}," start_copy",[40,460,461],{"class":106},"(new_name, conflict",[40,463,168],{"class":102},[40,465,466],{"class":50},"\"fail\"",[40,468,469],{"class":106},"):\n",[40,471,472,475,477,480,483,486,489,492,494,496,498,501,503,506,508],{"class":42,"line":110},[40,473,474],{"class":106},"    body ",[40,476,168],{"class":102},[40,478,479],{"class":106}," {",[40,481,482],{"class":50},"\"parentReference\"",[40,484,485],{"class":106},": {",[40,487,488],{"class":50},"\"driveId\"",[40,490,491],{"class":106},": ",[40,493,154],{"class":141},[40,495,275],{"class":106},[40,497,356],{"class":50},[40,499,500],{"class":106},": folder[",[40,502,356],{"class":50},[40,504,505],{"class":106},"]}, ",[40,507,362],{"class":50},[40,509,510],{"class":106},": new_name}\n",[40,512,513,515,517,520,522,524,526,528,530,532,534,536,538,541,543,545,548,550,552,555],{"class":42,"line":117},[40,514,234],{"class":106},[40,516,168],{"class":102},[40,518,519],{"class":106}," session.post(",[40,521,242],{"class":102},[40,523,245],{"class":50},[40,525,195],{"class":194},[40,527,142],{"class":141},[40,529,207],{"class":194},[40,531,254],{"class":50},[40,533,195],{"class":194},[40,535,154],{"class":141},[40,537,207],{"class":194},[40,539,540],{"class":50},"\u002Fitems\u002F",[40,542,195],{"class":194},[40,544,375],{"class":106},[40,546,547],{"class":50},"'id'",[40,549,204],{"class":106},[40,551,207],{"class":194},[40,553,554],{"class":50},"\u002Fcopy\"",[40,556,557],{"class":106},",\n",[40,559,560,563,565,567,570,573,576,578,581,583,585,587],{"class":42,"line":131},[40,561,562],{"class":278},"                     params",[40,564,168],{"class":102},[40,566,195],{"class":106},[40,568,569],{"class":50},"\"@microsoft.graph.conflictBehavior\"",[40,571,572],{"class":106},": conflict}, ",[40,574,575],{"class":278},"json",[40,577,168],{"class":102},[40,579,580],{"class":106},"body, ",[40,582,279],{"class":278},[40,584,168],{"class":102},[40,586,284],{"class":141},[40,588,287],{"class":106},[40,590,591,594,597,600,603],{"class":42,"line":138},[40,592,593],{"class":102},"    if",[40,595,596],{"class":106}," r.status_code ",[40,598,599],{"class":102},"==",[40,601,602],{"class":141}," 409",[40,604,605],{"class":106},":\n",[40,607,608,611,614],{"class":42,"line":151},[40,609,610],{"class":102},"        raise",[40,612,613],{"class":141}," FileExistsError",[40,615,616],{"class":106},"(new_name)\n",[40,618,619],{"class":42,"line":162},[40,620,293],{"class":106},[40,622,623,625,628,631],{"class":42,"line":174},[40,624,299],{"class":102},[40,626,627],{"class":106}," r.headers[",[40,629,630],{"class":50},"\"Location\"",[40,632,633],{"class":106},"]\n",[40,635,636],{"class":42,"line":213},[40,637,135],{"emptyLinePlaceholder":134},[40,639,640,643,645,648,651],{"class":42,"line":218},[40,641,642],{"class":106},"monitor_url ",[40,644,168],{"class":102},[40,646,647],{"class":106}," start_copy(",[40,649,650],{"class":50},"\"Project PX-124 tracker.xlsx\"",[40,652,287],{"class":106},[40,654,655,657,659,662],{"class":42,"line":231},[40,656,344],{"class":141},[40,658,347],{"class":106},[40,660,661],{"class":50},"\"202 accepted; monitor:\"",[40,663,664],{"class":106},", monitor_url)\n",[40,666,667,670],{"class":42,"line":290},[40,668,669],{"class":102},"try",[40,671,605],{"class":106},[40,673,674,677,680],{"class":42,"line":296},[40,675,676],{"class":106},"    start_copy(",[40,678,679],{"class":50},"\"Project PX-118 tracker.xlsx\"",[40,681,287],{"class":106},[40,683,684,687,689,692],{"class":42,"line":305},[40,685,686],{"class":102},"except",[40,688,613],{"class":141},[40,690,691],{"class":102}," as",[40,693,694],{"class":106}," e:\n",[40,696,697,700,702,705],{"class":42,"line":310},[40,698,699],{"class":141},"    print",[40,701,347],{"class":106},[40,703,704],{"class":50},"\"already exists, not copied:\"",[40,706,707],{"class":106},", e)\n",[10,709,710,711,714,715,718,719,722],{},"Asking for ",[37,712,713],{},"conflictBehavior=fail"," is a safe default for template copies: if a tracker for that project already exists, someone may have filled it in, and overwriting it with a blank template would be a serious loss. ",[37,716,717],{},"rename"," keeps both (adding a number to the new name) and ",[37,720,721],{},"replace"," overwrites; choose either deliberately. The copy starts with a version history of its own — the template's past versions are not carried over — so the history of each project's workbook begins on the day it was created, which is usually what auditors and owners expect.",[10,724,725,726,23],{},"Name the copies from data — project code, client, period — so the same input always produces the same file name and a rerun refuses rather than duplicates. The approach is discussed in ",[19,727,729],{"href":728},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation\u002Fmake-an-excel-report-job-safe-to-rerun\u002F","Make an Excel Report Job Safe to Rerun",[731,732,740,741,740,745,740,749,740,757,740,767,740,774,740,779,740,784,740,787,740,791,740,796,740,798,740,800,740,805,740,810,740,818,740,824,740,829,740,832,740,839,740,844,740,848,740,851,740,856,740,860,740,864,740,867,740,872,740,876,740,879,740,882,740,887,740,891,740,894,740,897,740,904],"svg",{"viewBox":733,"role":734,"ariaLabelledBy":735,"xmlns":738,"style":739},"4 0 752 388","img",[736,737],"sp-copy-t","sp-copy-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[742,743,744],"title",{"id":736},"An asynchronous copy in SharePoint",[746,747,748],"desc",{"id":737},"The script posts to the template's copy action and gets 202 with a monitor URL. It polls the monitor URL without the Graph token until the status is completed, which returns the new item's resourceId, then fetches the new item from Graph.",[750,751],"rect",{"x":752,"y":753,"width":754,"height":755,"fill":756},"4","0","752","388","#ffffff",[750,758],{"x":759,"y":760,"width":761,"height":762,"rx":763,"fill":764,"stroke":765,"style":766},"20.0","16.0","180.0","48.0","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[768,769,773],"text",{"x":770,"y":771,"style":772},"110.0","44.5","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Script",[750,775],{"x":776,"y":760,"width":761,"height":762,"rx":763,"fill":777,"stroke":778,"style":766},"290.0","#e7ebef","var(--line,#cdd5e6)",[768,780,783],{"x":781,"y":771,"style":782},"380.0","font-size:13px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Graph",[750,785],{"x":786,"y":760,"width":761,"height":762,"rx":763,"fill":777,"stroke":778,"style":766},"560.0",[768,788,790],{"x":789,"y":771,"style":782},"650.0","Monitor URL",[42,792],{"x1":770,"y1":793,"x2":770,"y2":794,"stroke":778,"style":795},"64.0","372.0","stroke-width:1.5px;stroke-dasharray:5 4",[42,797],{"x1":781,"y1":793,"x2":781,"y2":794,"stroke":778,"style":795},[42,799],{"x1":789,"y1":793,"x2":789,"y2":794,"stroke":778,"style":795},[42,801],{"x1":802,"y1":803,"x2":804,"y2":803,"stroke":765,"style":766},"115.0","104.0","371.0",[806,807],"polygon",{"points":808,"fill":809},"375.0,104.0 366.0,109.0 366.0,99.0","#5b5cf0",[750,811],{"x":812,"y":813,"width":814,"height":815,"rx":816,"fill":756,"stroke":778,"style":817},"144.5","75.0","201.0","21.0","10","stroke-width:1.2px",[768,819,823],{"x":820,"y":821,"style":822},"245.0","89.5","font-size:11.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","1. POST items\u002F{template}\u002Fcopy",[42,825],{"x1":826,"y1":827,"x2":828,"y2":827,"stroke":765,"style":766},"375.0","152.0","119.0",[806,830],{"points":831,"fill":809},"115.0,152.0 124.0,147.0 124.0,157.0",[750,833],{"x":834,"y":835,"width":836,"height":815,"rx":816,"fill":837,"stroke":838,"style":817},"184.6","123.0","120.7","#fdefd8","var(--gold,#b4740a)",[768,840,843],{"x":820,"y":841,"style":842},"137.5","font-size:11.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","2. 202 + Location",[42,845],{"x1":802,"y1":846,"x2":847,"y2":846,"stroke":765,"style":766},"200.0","641.0",[806,849],{"points":850,"fill":809},"645.0,200.0 636.0,205.0 636.0,195.0",[750,852],{"x":853,"y":854,"width":855,"height":815,"rx":816,"fill":756,"stroke":778,"style":817},"319.8","171.0","120.4",[768,857,859],{"x":781,"y":858,"style":822},"185.5","3. GET (no token)",[42,861],{"x1":862,"y1":863,"x2":828,"y2":863,"stroke":765,"style":766},"645.0","248.0",[806,865],{"points":866,"fill":809},"115.0,248.0 124.0,243.0 124.0,253.0",[750,868],{"x":869,"y":870,"width":871,"height":815,"rx":816,"fill":777,"stroke":778,"style":817},"315.7","219.0","128.7",[768,873,875],{"x":781,"y":874,"style":822},"233.5","4. inProgress 40%",[42,877],{"x1":802,"y1":878,"x2":847,"y2":878,"stroke":765,"style":766},"296.0",[806,880],{"points":881,"fill":809},"645.0,296.0 636.0,301.0 636.0,291.0",[750,883],{"x":884,"y":885,"width":886,"height":815,"rx":816,"fill":756,"stroke":778,"style":817},"333.9","267.0","92.2",[768,888,890],{"x":781,"y":889,"style":822},"281.5","5. GET again",[42,892],{"x1":862,"y1":893,"x2":828,"y2":893,"stroke":765,"style":766},"344.0",[806,895],{"points":896,"fill":809},"115.0,344.0 124.0,339.0 124.0,349.0",[750,898],{"x":899,"y":900,"width":901,"height":815,"rx":816,"fill":902,"stroke":903,"style":817},"293.3","315.0","173.5","#d9f4f1","var(--teal,#0f9488)",[768,905,908],{"x":781,"y":906,"style":907},"329.5","font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","6. completed + resourceId",[25,910,912],{"id":911},"step-3-poll-the-monitor-until-the-copy-completes","Step 3 — Poll the monitor until the copy completes",[10,914,915,916,919,920,923],{},"The monitor URL reports progress. It is pre-authenticated and not a Graph endpoint, so call it ",[14,917,918],{},"without"," the bearer token — a plain ",[37,921,922],{},"requests.get"," — and wait between calls:",[30,925,927],{"className":93,"code":926,"language":95,"meta":35,"style":35},"def wait_for_copy(url, timeout_s=120, interval=0.2):\n    deadline = time.monotonic() + timeout_s\n    while time.monotonic() \u003C deadline:\n        status = requests.get(url, timeout=30).json()\n        print(f\"  {status['status']:\u003C11} {status.get('percentageComplete', 0):>5.0f}%\")\n        if status[\"status\"] == \"completed\":\n            return status[\"resourceId\"]\n        if status[\"status\"] == \"failed\":\n            raise RuntimeError(f\"copy failed: {status.get('error')}\")\n        time.sleep(interval)\n    raise TimeoutError(\"copy did not finish in time\")\n\nnew_id = wait_for_copy(monitor_url)\nnew_item = session.get(f\"{GRAPH}\u002Fdrives\u002F{DRIVE}\u002Fitems\u002F{new_id}\", timeout=60).json()\nprint(new_item[\"name\"], new_item[\"webUrl\"])\n",[37,928,929,954,970,983,1003,1055,1075,1087,1104,1134,1139,1154,1158,1168,1216],{"__ignoreMap":35},[40,930,931,933,936,939,941,944,947,949,952],{"class":42,"line":43},[40,932,221],{"class":102},[40,934,935],{"class":224}," wait_for_copy",[40,937,938],{"class":106},"(url, timeout_s",[40,940,168],{"class":102},[40,942,943],{"class":141},"120",[40,945,946],{"class":106},", interval",[40,948,168],{"class":102},[40,950,951],{"class":141},"0.2",[40,953,469],{"class":106},[40,955,956,959,961,964,967],{"class":42,"line":110},[40,957,958],{"class":106},"    deadline ",[40,960,168],{"class":102},[40,962,963],{"class":106}," time.monotonic() ",[40,965,966],{"class":102},"+",[40,968,969],{"class":106}," timeout_s\n",[40,971,972,975,977,980],{"class":42,"line":117},[40,973,974],{"class":102},"    while",[40,976,963],{"class":106},[40,978,979],{"class":102},"\u003C",[40,981,982],{"class":106}," deadline:\n",[40,984,985,988,990,993,995,997,1000],{"class":42,"line":131},[40,986,987],{"class":106},"        status ",[40,989,168],{"class":102},[40,991,992],{"class":106}," requests.get(url, ",[40,994,279],{"class":278},[40,996,168],{"class":102},[40,998,999],{"class":141},"30",[40,1001,1002],{"class":106},").json()\n",[40,1004,1005,1008,1010,1012,1015,1017,1020,1023,1025,1028,1030,1032,1035,1038,1040,1042,1045,1048,1050,1053],{"class":42,"line":138},[40,1006,1007],{"class":141},"        print",[40,1009,347],{"class":106},[40,1011,242],{"class":102},[40,1013,1014],{"class":50},"\"  ",[40,1016,195],{"class":194},[40,1018,1019],{"class":106},"status[",[40,1021,1022],{"class":50},"'status'",[40,1024,204],{"class":106},[40,1026,1027],{"class":102},":\u003C11",[40,1029,207],{"class":194},[40,1031,479],{"class":194},[40,1033,1034],{"class":106},"status.get(",[40,1036,1037],{"class":50},"'percentageComplete'",[40,1039,275],{"class":106},[40,1041,753],{"class":141},[40,1043,1044],{"class":106},")",[40,1046,1047],{"class":102},":>5.0f",[40,1049,207],{"class":194},[40,1051,1052],{"class":50},"%\"",[40,1054,287],{"class":106},[40,1056,1057,1060,1063,1066,1068,1070,1073],{"class":42,"line":151},[40,1058,1059],{"class":102},"        if",[40,1061,1062],{"class":106}," status[",[40,1064,1065],{"class":50},"\"status\"",[40,1067,183],{"class":106},[40,1069,599],{"class":102},[40,1071,1072],{"class":50}," \"completed\"",[40,1074,605],{"class":106},[40,1076,1077,1080,1082,1085],{"class":42,"line":162},[40,1078,1079],{"class":102},"            return",[40,1081,1062],{"class":106},[40,1083,1084],{"class":50},"\"resourceId\"",[40,1086,633],{"class":106},[40,1088,1089,1091,1093,1095,1097,1099,1102],{"class":42,"line":174},[40,1090,1059],{"class":102},[40,1092,1062],{"class":106},[40,1094,1065],{"class":50},[40,1096,183],{"class":106},[40,1098,599],{"class":102},[40,1100,1101],{"class":50}," \"failed\"",[40,1103,605],{"class":106},[40,1105,1106,1109,1112,1114,1116,1119,1121,1123,1126,1128,1130,1132],{"class":42,"line":213},[40,1107,1108],{"class":102},"            raise",[40,1110,1111],{"class":141}," RuntimeError",[40,1113,347],{"class":106},[40,1115,242],{"class":102},[40,1117,1118],{"class":50},"\"copy failed: ",[40,1120,195],{"class":194},[40,1122,1034],{"class":106},[40,1124,1125],{"class":50},"'error'",[40,1127,1044],{"class":106},[40,1129,207],{"class":194},[40,1131,245],{"class":50},[40,1133,287],{"class":106},[40,1135,1136],{"class":42,"line":218},[40,1137,1138],{"class":106},"        time.sleep(interval)\n",[40,1140,1141,1144,1147,1149,1152],{"class":42,"line":231},[40,1142,1143],{"class":102},"    raise",[40,1145,1146],{"class":141}," TimeoutError",[40,1148,347],{"class":106},[40,1150,1151],{"class":50},"\"copy did not finish in time\"",[40,1153,287],{"class":106},[40,1155,1156],{"class":42,"line":290},[40,1157,135],{"emptyLinePlaceholder":134},[40,1159,1160,1163,1165],{"class":42,"line":296},[40,1161,1162],{"class":106},"new_id ",[40,1164,168],{"class":102},[40,1166,1167],{"class":106}," wait_for_copy(monitor_url)\n",[40,1169,1170,1173,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193,1195,1197,1199,1202,1204,1206,1208,1210,1212,1214],{"class":42,"line":305},[40,1171,1172],{"class":106},"new_item ",[40,1174,168],{"class":102},[40,1176,239],{"class":106},[40,1178,242],{"class":102},[40,1180,245],{"class":50},[40,1182,195],{"class":194},[40,1184,142],{"class":141},[40,1186,207],{"class":194},[40,1188,254],{"class":50},[40,1190,195],{"class":194},[40,1192,154],{"class":141},[40,1194,207],{"class":194},[40,1196,540],{"class":50},[40,1198,195],{"class":194},[40,1200,1201],{"class":106},"new_id",[40,1203,207],{"class":194},[40,1205,245],{"class":50},[40,1207,275],{"class":106},[40,1209,279],{"class":278},[40,1211,168],{"class":102},[40,1213,284],{"class":141},[40,1215,1002],{"class":106},[40,1217,1218,1220,1223,1225,1228,1231],{"class":42,"line":310},[40,1219,344],{"class":141},[40,1221,1222],{"class":106},"(new_item[",[40,1224,362],{"class":50},[40,1226,1227],{"class":106},"], new_item[",[40,1229,1230],{"class":50},"\"webUrl\"",[40,1232,413],{"class":106},[10,1234,1235,1236,1239,1240,1243,1244,65,1247,1250,1251,1254],{},"The status moves through ",[37,1237,1238],{},"notStarted"," and ",[37,1241,1242],{},"inProgress"," to ",[37,1245,1246],{},"completed",[37,1248,1249],{},"failed","), and on completion the response includes ",[37,1252,1253],{},"resourceId",", the new item's ID. A short interval works for the mock; against SharePoint, poll every second or two and back off for large files — copies of ordinary workbooks usually finish within a few seconds. Sending the Graph token to the monitor URL can make it reject the request, the same rule as for upload-session URLs. The timeout matters: without one, a stuck copy holds a scheduled job forever.",[731,1256,740,1261,740,1264,740,1267,740,1270,740,1277,740,1281,740,1286,740,1291,740,1295,740,1299,740,1302,740,1307,740,1311,740,1313,740,1315,740,1318,740,1322,740,1325,740,1327,740,1329,740,1332,740,1337,740,1340,740,1342],{"viewBox":1257,"role":734,"ariaLabelledBy":1258,"xmlns":738,"style":739},"4 0 752 211",[1259,1260],"sp-status-t","sp-status-d",[742,1262,1263],{"id":1259},"The states a copy passes through",[746,1265,1266],{"id":1260},"A copy starts as notStarted, moves to inProgress with a rising percentage, and ends either completed with a resourceId for the new file or failed with an error. A script should stop polling after a timeout.",[750,1268],{"x":752,"y":753,"width":754,"height":1269,"fill":756},"211",[42,1271],{"x1":1272,"y1":1273,"x2":1274,"y2":1273,"stroke":1275,"style":1276},"94.0","105.5","666.0","var(--muted,#5b6780)","stroke-width:2.5px",[750,1278],{"x":759,"y":760,"width":1279,"height":1280,"rx":763,"fill":777,"stroke":778,"style":766},"172.0","57.5",[768,1282,1238],{"x":1283,"y":1284,"style":1285},"106.0","40.5","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle",[768,1287,1290],{"x":1283,"y":1288,"style":1289},"59.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","queued",[42,1292],{"x1":1283,"y1":1293,"x2":1283,"y2":1273,"stroke":1275,"style":1294},"73.5","stroke-width:1.5px",[1296,1297],"circle",{"cx":1283,"cy":1273,"r":1298,"fill":1275,"stroke":756,"style":766},"7.0",[750,1300],{"x":1301,"y":841,"width":1279,"height":1280,"rx":763,"fill":837,"stroke":838,"style":766},"202.7",[768,1303,1242],{"x":1304,"y":1305,"style":1306},"288.7","162.0","font-size:12.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[768,1308,1310],{"x":1304,"y":1309,"style":1289},"180.5","percentageComplete rises",[42,1312],{"x1":1304,"y1":1273,"x2":1304,"y2":841,"stroke":1275,"style":1294},[1296,1314],{"cx":1304,"cy":1273,"r":1298,"fill":838,"stroke":756,"style":766},[750,1316],{"x":1317,"y":760,"width":1279,"height":1280,"rx":763,"fill":902,"stroke":903,"style":766},"385.3",[768,1319,1246],{"x":1320,"y":1284,"style":1321},"471.3","font-size:12.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[768,1323,1324],{"x":1320,"y":1288,"style":1289},"resourceId of the copy",[42,1326],{"x1":1320,"y1":1293,"x2":1320,"y2":1273,"stroke":1275,"style":1294},[1296,1328],{"cx":1320,"cy":1273,"r":1298,"fill":903,"stroke":756,"style":766},[750,1330],{"x":1331,"y":841,"width":1279,"height":1280,"rx":763,"fill":764,"stroke":765,"style":766},"568.0",[768,1333,1336],{"x":1334,"y":1305,"style":1335},"654.0","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","failed \u002F timeout",[768,1338,1339],{"x":1334,"y":1309,"style":1289},"raise and report",[42,1341],{"x1":1334,"y1":1273,"x2":1334,"y2":841,"stroke":1275,"style":1294},[1296,1343],{"cx":1334,"cy":1273,"r":1298,"fill":765,"stroke":756,"style":766},[25,1345,1347],{"id":1346},"step-4-fill-in-the-copy-without-downloading-it","Step 4 — Fill in the copy without downloading it",[10,1349,1350],{},"With the new item's ID, write the values that make this copy specific — project code, owner, start date — straight into named cells through the workbook API:",[30,1352,1354],{"className":93,"code":1353,"language":95,"meta":35,"style":35},"from datetime import date\n\nBOOK = f\"{GRAPH}\u002Fdrives\u002F{DRIVE}\u002Fitems\u002F{new_id}\u002Fworkbook\"\nvalues = [[\"PX-124\"], [\"Priya Natarajan\"], [date(2026, 10, 5).isoformat()]]\nr = session.patch(f\"{BOOK}\u002Fworksheets('Summary')\u002Frange(address='B2:B4')\", json={\"values\": values}, timeout=60)\nr.raise_for_status()\nprint(\"wrote\", patched[-1])\n",[37,1355,1356,1368,1372,1408,1445,1490,1495],{"__ignoreMap":35},[40,1357,1358,1360,1363,1365],{"class":42,"line":43},[40,1359,120],{"class":102},[40,1361,1362],{"class":106}," datetime ",[40,1364,103],{"class":102},[40,1366,1367],{"class":106}," date\n",[40,1369,1370],{"class":42,"line":110},[40,1371,135],{"emptyLinePlaceholder":134},[40,1373,1374,1377,1379,1381,1383,1385,1387,1389,1391,1393,1395,1397,1399,1401,1403,1405],{"class":42,"line":117},[40,1375,1376],{"class":141},"BOOK",[40,1378,145],{"class":102},[40,1380,188],{"class":102},[40,1382,245],{"class":50},[40,1384,195],{"class":194},[40,1386,142],{"class":141},[40,1388,207],{"class":194},[40,1390,254],{"class":50},[40,1392,195],{"class":194},[40,1394,154],{"class":141},[40,1396,207],{"class":194},[40,1398,540],{"class":50},[40,1400,195],{"class":194},[40,1402,1201],{"class":106},[40,1404,207],{"class":194},[40,1406,1407],{"class":50},"\u002Fworkbook\"\n",[40,1409,1410,1413,1415,1418,1421,1424,1427,1430,1433,1435,1437,1439,1442],{"class":42,"line":131},[40,1411,1412],{"class":106},"values ",[40,1414,168],{"class":102},[40,1416,1417],{"class":106}," [[",[40,1419,1420],{"class":50},"\"PX-124\"",[40,1422,1423],{"class":106},"], [",[40,1425,1426],{"class":50},"\"Priya Natarajan\"",[40,1428,1429],{"class":106},"], [date(",[40,1431,1432],{"class":141},"2026",[40,1434,275],{"class":106},[40,1436,816],{"class":141},[40,1438,275],{"class":106},[40,1440,1441],{"class":141},"5",[40,1443,1444],{"class":106},").isoformat()]]\n",[40,1446,1447,1450,1452,1455,1457,1459,1461,1463,1465,1468,1470,1472,1474,1476,1479,1482,1484,1486,1488],{"class":42,"line":138},[40,1448,1449],{"class":106},"r ",[40,1451,168],{"class":102},[40,1453,1454],{"class":106}," session.patch(",[40,1456,242],{"class":102},[40,1458,245],{"class":50},[40,1460,195],{"class":194},[40,1462,1376],{"class":141},[40,1464,207],{"class":194},[40,1466,1467],{"class":50},"\u002Fworksheets('Summary')\u002Frange(address='B2:B4')\"",[40,1469,275],{"class":106},[40,1471,575],{"class":278},[40,1473,168],{"class":102},[40,1475,195],{"class":106},[40,1477,1478],{"class":50},"\"values\"",[40,1480,1481],{"class":106},": values}, ",[40,1483,279],{"class":278},[40,1485,168],{"class":102},[40,1487,284],{"class":141},[40,1489,287],{"class":106},[40,1491,1492],{"class":42,"line":151},[40,1493,1494],{"class":106},"r.raise_for_status()\n",[40,1496,1497,1499,1501,1504,1507,1510,1513],{"class":42,"line":162},[40,1498,344],{"class":141},[40,1500,347],{"class":106},[40,1502,1503],{"class":50},"\"wrote\"",[40,1505,1506],{"class":106},", patched[",[40,1508,1509],{"class":102},"-",[40,1511,1512],{"class":141},"1",[40,1514,413],{"class":106},[10,1516,1517,1518,23],{},"Because the template's formulas, data validation, conditional formatting and protection stay in SharePoint and are never round-tripped through a Python library, nothing is lost — a real advantage over download-edit-upload when the template uses features openpyxl does not preserve, such as slicers or Power Query connections. A date written as an ISO string is parsed by Excel into a date value as if typed, so format the template cell for dates. For more than a handful of cells, write whole ranges in one call rather than one request per cell, and if the template's formulas need recalculating before anyone reads results back, use a workbook session as described in ",[19,1519,1521],{"href":1520},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions\u002F","Recalculate Excel Online Workbooks with Graph Sessions",[10,1523,1524,1525,1528,1529,1533,1534,1537],{},"Keep the cells the script writes in one clearly marked area — a \"Summary\" or \"Setup\" sheet with labelled input cells — rather than scattered across the template. Then the template's owner can change layout elsewhere without breaking the automation, and the addresses the script uses can be documented in one place, or better still replaced with named ranges: ",[37,1526,1527],{},"workbook\u002Fnames('ProjectCode')\u002Frange"," resolves the address inside the workbook, so moving the cell in the template does not require a code change. If many copies are made in one run, note that each copy and each write counts towards Graph's throttling limits; ",[19,1530,1532],{"href":1531},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fhandle-microsoft-graph-throttling-for-excel\u002F","Handle Microsoft Graph Throttling for Excel"," covers retrying on HTTP 429 with the ",[37,1535,1536],{},"Retry-After"," delay.",[731,1539,740,1544,740,1547,740,1550,740,1553,740,1557,740,1568,740,1574,740,1580,740,1585,740,1589,740,1593,740,1597,740,1600,740,1607,740,1612,740,1616,740,1619,740,1622,740,1625],{"viewBox":1540,"role":734,"ariaLabelledBy":1541,"xmlns":738,"style":739},"4 0 752 233",[1542,1543],"sp-vs-t","sp-vs-d",[742,1545,1546],{"id":1542},"Copy in SharePoint or download, edit and upload",[746,1548,1549],{"id":1543},"A server-side copy moves no bytes through the script, preserves every Excel feature and is filled in through the workbook API. Download, edit and upload works anywhere but routes the file through Python, which may drop features the library does not support.",[750,1551],{"x":752,"y":753,"width":754,"height":1552,"fill":756},"233",[750,1554],{"x":759,"y":760,"width":1555,"height":814,"rx":1556,"fill":902,"stroke":903,"style":766},"351.0","14",[1558,1559,1561],"g",{"transform":1560},"translate(182.5,29.0) scale(1.083)",[1562,1563],"path",{"d":1564,"fill":1565,"stroke":1566,"style":1567},"M7 18h10a4 4 0 0 0 .6-8A6 6 0 0 0 6.1 11.4 3.4 3.4 0 0 0 7 18z","none","var(--teal-ink,#0b6157)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[768,1569,1573],{"x":1570,"y":1571,"style":1572},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Server-side copy",[42,1575],{"x1":1576,"y1":1577,"x2":1578,"y2":1577,"stroke":903,"style":1579},"36.0","91.0","355.0","stroke-width:1px",[768,1581,1584],{"x":1570,"y":1582,"style":1583},"111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","no bytes through the script",[768,1586,1588],{"x":1570,"y":1587,"style":1583},"137.0","every feature preserved",[768,1590,1592],{"x":1570,"y":1591,"style":1583},"163.0","fill cells via workbook API",[768,1594,1596],{"x":1570,"y":1595,"style":1583},"189.0","asynchronous - poll to finish",[750,1598],{"x":1599,"y":760,"width":1555,"height":814,"rx":1556,"fill":837,"stroke":838,"style":766},"389.0",[1558,1601,1603],{"transform":1602},"translate(551.5,29.0) scale(1.083)",[1562,1604],{"d":1605,"fill":1565,"stroke":1606,"style":1567},"M20 11a8 8 0 1 0-2.3 5.7 M20 4v7h-7","var(--gold-ink,#7a4e06)",[768,1608,1611],{"x":1609,"y":1571,"style":1610},"564.5","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Download-edit-upload",[42,1613],{"x1":1614,"y1":1577,"x2":1615,"y2":1577,"stroke":838,"style":1579},"405.0","724.0",[768,1617,1618],{"x":1609,"y":1582,"style":1583},"works with any storage",[768,1620,1621],{"x":1609,"y":1587,"style":1583},"full control in Python",[768,1623,1624],{"x":1609,"y":1591,"style":1583},"may drop unsupported features",[768,1626,1627],{"x":1609,"y":1595,"style":1583},"more bandwidth and code",[25,1629,1631],{"id":1630},"common-pitfalls","Common pitfalls",[1633,1634,1635,1645,1651,1657,1665],"ul",{},[1636,1637,1638,1642,1643,23],"li",{},[1639,1640,1641],"strong",{},"Overwriting a filled-in copy."," Default to ",[37,1644,713],{},[1636,1646,1647,1650],{},[1639,1648,1649],{},"Treating the 202 as \"done\"."," The copy is asynchronous; poll the monitor URL.",[1636,1652,1653,1656],{},[1639,1654,1655],{},"Sending the Graph token to the monitor URL."," It is pre-authenticated.",[1636,1658,1659,1662,1663,23],{},[1639,1660,1661],{},"Polling forever."," Set a timeout and handle ",[37,1664,1249],{},[1636,1666,1667,1670],{},[1639,1668,1669],{},"Hard-coding item IDs."," Resolve paths at runtime so renames give clear errors.",[25,1672,1674],{"id":1673},"conclusion","Conclusion",[10,1676,1677,1678,434,1680,1682,1683,1685,1686,1688,1689,1691,1692,1695],{},"To create workbooks from a template in SharePoint, resolve the template and destination by path, ",[37,1679,433],{},[37,1681,437],{}," action with a ",[37,1684,422],{}," and the new name, and poll the ",[37,1687,445],{}," monitor URL — without the Graph token — until the status is ",[37,1690,1246],{}," and you have the new item's ID. Then fill in the copy through the workbook API. The template's features stay intact because the file never leaves SharePoint, and setting the conflict behaviour to ",[37,1693,1694],{},"fail"," makes reruns safe.",[25,1697,1699],{"id":1698},"frequently-asked-questions","Frequently asked questions",[10,1701,1702,1705],{},[1639,1703,1704],{},"How do I copy a file in SharePoint with Microsoft Graph?","\nPOST to the copy action on the drive item with a parentReference holding the destination drive and folder IDs and an optional new name; Graph returns 202 with a monitor URL.",[10,1707,1708,1711],{},[1639,1709,1710],{},"How do I know when a Graph copy has finished?","\nPoll the URL in the Location header of the 202 response until its status is completed; the response then contains the resourceId of the new item.",[10,1713,1714,1717],{},[1639,1715,1716],{},"Why does polling the copy monitor URL return 401?","\nThe monitor URL is pre-authenticated; call it without the Authorization header used for Graph requests.",[10,1719,1720,1723],{},[1639,1721,1722],{},"Can I copy a template to a different SharePoint site?","\nYes. Set the driveId in parentReference to the destination library's drive ID, which can belong to another site.",[25,1725,1727],{"id":1726},"related","Related",[1633,1729,1730,1736,1743,1750],{},[1636,1731,1732,1733,1735],{},"Up one level: ",[19,1734,22],{"href":21}," — Excel in the cloud from Python.",[1636,1737,1738,1742],{},[19,1739,1741],{"href":1740},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcreate-a-new-workbook-in-onedrive-with-microsoft-graph\u002F","Create a New Workbook in OneDrive with Microsoft Graph"," — uploading a workbook built in Python.",[1636,1744,1745,1749],{},[19,1746,1748],{"href":1747},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fshare-an-excel-file-in-onedrive-with-a-link-using-graph\u002F","Share an Excel File in OneDrive with a Link Using Graph"," — sending the copy to people.",[1636,1751,1752,1756],{},[19,1753,1755],{"href":1754},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fread-and-write-excel-online-ranges-with-microsoft-graph\u002F","Read and Write Excel Online Ranges with Microsoft Graph"," — more on the workbook API.",[1758,1759,1760],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}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);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}",{"title":35,"searchDepth":110,"depth":110,"links":1762},[1763,1764,1765,1766,1767,1768,1769,1770,1771],{"id":27,"depth":110,"text":28},{"id":86,"depth":110,"text":87},{"id":426,"depth":110,"text":427},{"id":911,"depth":110,"text":912},{"id":1346,"depth":110,"text":1347},{"id":1630,"depth":110,"text":1631},{"id":1673,"depth":110,"text":1674},{"id":1698,"depth":110,"text":1699},{"id":1726,"depth":110,"text":1727},"2026-10-02","Create new Excel workbooks from a template that lives in SharePoint or OneDrive using Microsoft Graph from Python: find the template by path, start a server-side copy into a destination folder, poll the asynchronous monitor URL until the copy finishes, handle name clashes, then fill in the copy through the workbook API without downloading anything.","md",[1776,1778,1780,1782],{"q":1704,"a":1777},"POST to the copy action on the drive item with a parentReference holding the destination drive and folder IDs and an optional new name; Graph returns 202 with a monitor URL.",{"q":1710,"a":1779},"Poll the URL in the Location header of the 202 response until its status is completed; the response then contains the resourceId of the new item.",{"q":1716,"a":1781},"The monitor URL is pre-authenticated; call it without the Authorization header used for Graph requests.",{"q":1722,"a":1783},"Yes. Set the driveId in parentReference to the destination library's drive ID, which can belong to another site.","copy a template workbook in sharepoint with microsoft graph",null,{"breadcrumb":1787},[1788,1791,1794],{"name":1789,"item":1790},"Home","\u002F",{"name":1792,"item":1793},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":22,"item":21},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcopy-a-template-workbook-in-sharepoint-with-microsoft-graph",{"title":5,"description":1797},"Copy an Excel template in SharePoint with Microsoft Graph from Python: driveItem copy, parentReference, the 202 monitor URL, polling to completion, conflict handling and filling the copy via the workbook API.","copy-a-template-workbook-in-sharepoint-with-microsoft-graph","automating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcopy-a-template-workbook-in-sharepoint-with-microsoft-graph\u002Findex",[1801,1802],"excel-online","report-layouts","how-to","O8ZwX1NxkvLFmF9hBA867BvJoCBjh-bkf-n3-ClsOxo",[1806,1810],{"title":1807,"path":1808,"stem":1809,"children":-1},"Call Excel Worksheet Functions Through Microsoft Graph","\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcall-excel-worksheet-functions-through-microsoft-graph","automating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcall-excel-worksheet-functions-through-microsoft-graph\u002Findex",{"title":1741,"path":1811,"stem":1812,"children":-1},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcreate-a-new-workbook-in-onedrive-with-microsoft-graph","automating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcreate-a-new-workbook-in-onedrive-with-microsoft-graph\u002Findex",[1814,1816],{"path":1815,"title":22},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph",{"path":1817,"title":1792},"\u002Fautomating-reporting-workflows",[],[1820,1823,1826,1829,1832,1834],{"path":1821,"title":1822,"kind":1803},"\u002Fautomating-reporting-workflows\u002Femailing-excel-reports-with-smtplib\u002Fsend-an-excel-report-with-microsoft-graph-api","Send an Excel Report with the Microsoft Graph API",{"path":1824,"title":1825,"kind":1803},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-a-chart-to-an-excel-online-workbook-with-microsoft-graph","Add a Chart to an Excel Online Workbook with Microsoft Graph",{"path":1827,"title":1828,"kind":1803},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-rename-and-delete-worksheets-with-microsoft-graph","Add, Rename and Delete Worksheets with Microsoft Graph",{"path":1830,"title":1831,"kind":1803},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fappend-rows-to-an-excel-online-table-with-microsoft-graph","Append Rows to an Excel Online Table with Microsoft Graph",{"path":1833,"title":73,"kind":1803},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fauthenticate-to-microsoft-graph-with-msal-for-excel-automation",{"path":1808,"title":1807,"kind":1803},1790972118963]